
Design Pattern “Adapter” / “Adapter”
Read the description of other patterns.
Let's start.
To begin with, I will explain a few organizational issues.
As you already noticed, this section is optional in my posts about patterns. But I could not start writing about a specific pattern without explaining why they were actually needed.
Not so long ago, when I was in junior courses, to my saying, “Yes, we already know how to write programs!”, My colleague said, “The maximum that you can write is algorithms, but not programs.” I recall these words before still with a smile on his face. He was absolutely right, everything we were taught in 3 years (I was in my third year then) - the implementation of basic algorithms from Cormen / Knut. We really could write these algorithms, we could write functions, procedures that implement them. They could even write a class in C ++ or Java in which to describe all the logic of working with this class of objects. But when such classes became two or even three :) problems began. And with any attempt to write any “program”, the invention of the bicycle began (I think that everyone who reads this post invented several such bikes himself). Then, I began to suspect
It turned out that there is such a base - these are design patterns. By and large, a pattern is a typical solution to common design problems . Patterns solve a number of major programming / design issues. This is the invention of the bicycle, the problem of reusing code, the problem of maintaining the code.
Of course, design patterns if they do not solve, then simplify the solution of these problems. So, a developer or designer, knowing at least the basic set of patterns, uses them to replace invented bicycles. Code reuse is becoming more transparent and justified. And accompanying code written using patterns is at least understandable and not difficult.
Now, it seems to me that we can proceed to consider a specific pattern - the “Adapter”.
Ensure the interaction of objects with various interfaces. Adapt rather than rewrite existing code to the required interface.
The “Adapter” pattern, in fact, is one of the few that programmers put into practice without realizing it. The adapter can be found, perhaps, in any modern program system - be it a simple application or, for example, the Java API.
Let's take a closer look at the problem, for understanding what the adapter should look like. The problem, again, is code reuse. In other words, there is a client that can work with some interface, let's call it client. There is a class that, in principle, does what the client needs, but does not implement the client interface. Of course, programming a new class is a pretty waste of time and resources. Easier to adaptalready existing code in a form suitable for use by the client. For this, there is an adapter. Moreover, two types of adapters are shared - Object Adapter (adapter at the object level) and Class Adapter (adapter at the class level). We look at both, but in order.
For example, consider a simple situation. There is some class - SequenceGenerator, generating sequences of integers, according to a certain law - this is our client. There is an interface - Generator, which uses the client directly to generate each individual element of the sequence - this is our client interface. In addition, there is a class RandomGenerator, which already knows how to generate random numbers. Of course, SequenceGenerator cannot use RandomGenerator to generate elements, because it does not match the client interface. Our task is to write an adapter (in two ways) RandomGenerator to SequenceGenerator.


So, with class diagrams, let's talk about the differences between the adapter at the object level and the adapter at the class level. In fact, the differences are already visible from the name. In the first case, the adaptable object (RandomGenerator) is the field (link) in the adapter class (RandomGeneratorAdapter), in the second case it is the adapter through the use of the inheritance mechanism. In real projects, it is recommended to use the Object Adapter, due to its less connectivity with the adaptable object.
Consider the implementation of the task. I implemented Object Adapter in C ++, Class Adapter in Java.
The classic implementation of the Class Adapter pattern implies the use of multiple inheritance. In Java, you can take advantage of the implementation of interfaces. In my opinion, this is even somehow more correct.
That's all. Waiting for your feedback in the comments.
Let's start.
To begin with, I will explain a few organizational issues.
- The description of this or that pattern is my very personal interpretation of the theoretical and practical material collected from books and online articles;
- When constructing UML diagrams, I will use the free editor from astah , due to its simplicity and independence from a particular language or environment. At the same time, the diagrams will not differ in the abundance of pictures and colors, but they will clearly reflect the essence of the pattern;
- When implementing practical examples, the programming language will be chosen by chance. However, I will try to select those language tools on which this pattern is not implemented trivially;
- Each of my posts will contain at least 5 sections - Problem, Pattern Description, Practical Problem, Class Diagram and Implementation;
- If you disagree with something or you have additions to the material presented by me, I will be glad to read them in the comments. However, remember - I also study patterns with you :)
Intro
As you already noticed, this section is optional in my posts about patterns. But I could not start writing about a specific pattern without explaining why they were actually needed.
Not so long ago, when I was in junior courses, to my saying, “Yes, we already know how to write programs!”, My colleague said, “The maximum that you can write is algorithms, but not programs.” I recall these words before still with a smile on his face. He was absolutely right, everything we were taught in 3 years (I was in my third year then) - the implementation of basic algorithms from Cormen / Knut. We really could write these algorithms, we could write functions, procedures that implement them. They could even write a class in C ++ or Java in which to describe all the logic of working with this class of objects. But when such classes became two or even three :) problems began. And with any attempt to write any “program”, the invention of the bicycle began (I think that everyone who reads this post invented several such bikes himself). Then, I began to suspect
It turned out that there is such a base - these are design patterns. By and large, a pattern is a typical solution to common design problems . Patterns solve a number of major programming / design issues. This is the invention of the bicycle, the problem of reusing code, the problem of maintaining the code.
Of course, design patterns if they do not solve, then simplify the solution of these problems. So, a developer or designer, knowing at least the basic set of patterns, uses them to replace invented bicycles. Code reuse is becoming more transparent and justified. And accompanying code written using patterns is at least understandable and not difficult.
Now, it seems to me that we can proceed to consider a specific pattern - the “Adapter”.
Problem
Ensure the interaction of objects with various interfaces. Adapt rather than rewrite existing code to the required interface.
Description
The “Adapter” pattern, in fact, is one of the few that programmers put into practice without realizing it. The adapter can be found, perhaps, in any modern program system - be it a simple application or, for example, the Java API.
Let's take a closer look at the problem, for understanding what the adapter should look like. The problem, again, is code reuse. In other words, there is a client that can work with some interface, let's call it client. There is a class that, in principle, does what the client needs, but does not implement the client interface. Of course, programming a new class is a pretty waste of time and resources. Easier to adaptalready existing code in a form suitable for use by the client. For this, there is an adapter. Moreover, two types of adapters are shared - Object Adapter (adapter at the object level) and Class Adapter (adapter at the class level). We look at both, but in order.
Practical task
For example, consider a simple situation. There is some class - SequenceGenerator, generating sequences of integers, according to a certain law - this is our client. There is an interface - Generator, which uses the client directly to generate each individual element of the sequence - this is our client interface. In addition, there is a class RandomGenerator, which already knows how to generate random numbers. Of course, SequenceGenerator cannot use RandomGenerator to generate elements, because it does not match the client interface. Our task is to write an adapter (in two ways) RandomGenerator to SequenceGenerator.
Class diagrams
Object adapter

Class adapter

So, with class diagrams, let's talk about the differences between the adapter at the object level and the adapter at the class level. In fact, the differences are already visible from the name. In the first case, the adaptable object (RandomGenerator) is the field (link) in the adapter class (RandomGeneratorAdapter), in the second case it is the adapter through the use of the inheritance mechanism. In real projects, it is recommended to use the Object Adapter, due to its less connectivity with the adaptable object.
Implementation
Consider the implementation of the task. I implemented Object Adapter in C ++, Class Adapter in Java.
Object adapter
class Generator {
public:
virtual int next() = 0;
};
class SequenceGenerator {
private:
Generator *generator;
protected:
public:
SequenceGenerator(Generator& generator);
int* generate(int length);
};
SequenceGenerator::SequenceGenerator(Generator& generator) {
this->generator = &generator;
}
int* SequenceGenerator::generate(int length) {
int *ret = new int[length];
for (int i=0; i
ret[i] = this->generator->next();
}
return ret;
}
class RandomGenerator {
public:
inline int getRandomNumber() { return 4; }; // It`s really random number.
};
class RandomGeneratorAdapter : public Generator {
private:
RandomGenerator *adaptee;
public:
RandomGeneratorAdapter(RandomGenerator& adaptee);
virtual int next();
};
RandomGeneratorAdapter::RandomGeneratorAdapter(RandomGenerator& adaptee) {
this->adaptee = &adaptee;
}
int RandomGeneratorAdapter::next() {
return this->adaptee->getRandomNumber();
}
// Использование
int main(int argc, char *argv[]) {
RandomGenerator rgenerator;
RandomGeneratorAdapter adapter(rgenerator);
SequenceGenerator sgenerator(adapter);
const int SIZE = 10;
int *seq = sgenerator.generate(SIZE);
for (int i=0; i
cout << seq[i] << " ";
}
}
* This source code was highlighted with Source Code Highlighter.
Class adapter
The classic implementation of the Class Adapter pattern implies the use of multiple inheritance. In Java, you can take advantage of the implementation of interfaces. In my opinion, this is even somehow more correct.
public interface Generator {
public int next();
}
public class SequenceGenerator {
private Generator generator;
public SequenceGenerator(Generator generator) {
super();
this.generator = generator;
}
public int[] generate(int length) {
int ret[] = new int[length];
for (int i=0; i
ret[i] = generator.next();
}
return ret;
}
}
public class RandomGenerator {
public int getRandomNumber() {
return 4;
}
}
public class RandomGeneratorAdapter extends RandomGenerator implements Generator {
@Override
public int next() {
return getRandomNumber();
}
}
// Использование
public class Main {
public static void main(String[] args) {
RandomGeneratorAdapter adapter = new RandomGeneratorAdapter();
SequenceGenerator generator = new SequenceGenerator(adapter);
for (int i: generator.generate(10)) {
System.out.print(i + " ");
}
}
}
* This source code was highlighted with Source Code Highlighter.
That's all. Waiting for your feedback in the comments.