3.1 Model Motivation
In real life, there are often instances where two objects cannot work together because of incompatible interfaces, and a third party is needed to adapt. For example, a Chinese speaker needs a translator to talk to an English speaker, a power adapter to connect a dc laptop to AC power, and a card reader to access a camera’s SD memory card from a computer.
It can also occur in software design where components with certain business functions that need to be developed already exist in an existing component library, but they are incompatible with the current system’s interface specifications, and the adapter pattern is a good solution if the cost of redeveloping these components is high. Typically, a client can access the services it provides through the interface of the target class. Sometimes, an existing class can meet the functional needs of the customer class, but the interface it provides is not necessarily what the customer class expects. This may be because the name of the existing class’s method does not match the name of the method defined in the target class.
In this case, the existing interface needs to be transformed into the interface expected by the customer class, which ensures reuse of the existing class. Without this transformation, the client class cannot take advantage of the functionality provided by the existing class, and the adapter pattern can accomplish this transformation.
In the Adapter pattern, you can define a wrapper class that wraps objects with incompatible interfaces. This wrapper class refers to an Adapter, and the object it wraps is an Adaptee, that is, the class to be adapted.
The adapter provides the interface required by the client class, and the implementation of the adapter converts the request of the client class into a call to the appropriate interface of the adapter. That is, when a client class calls an adapter’s method, the adapter class’s method is called inside the adapter class, and the process is transparent to the client class, which does not directly access the adapter class. Thus, adapters enable classes to work together that cannot interact because of incompatible interfaces. This is the pattern motivation for the adapter pattern.
3.2 Definition and characteristics of the mode
An Adapter pattern is defined as follows: Converts the interface of one class into another interface that the customer expects, so that classes that would otherwise not work together can work together because of interface incompatibility. Adapter pattern is divided into class structure pattern and object structure pattern, the former class coupling degree is higher than the latter, and requires programmers to understand the internal structure of relevant components in the existing component library, so the application is relatively less. The main advantages of this mode are as follows.
- Increased class transparency and reusability, encapsulation of the specific implementation in the adapter class, transparent to the client class, and improved adapter reuse.
- Flexibility and extensibility are very good, through the use of configuration files, it is easy to replace the adapter, but also without modifying the original code on the basis of the new adapter class, in full compliance with the “open closed principle”.
- Decouple the target class from the adapter class and reuse the existing adapter class by introducing an adapter class without modifying the original code. Solve the problem of inconsistent interface between target class and adapter class.
The adapter-like pattern also has the following advantages:
Because the adapter class is a subclass of the adapter class, you can substitute some of the adapter methods in the adapter class to make the adapter more flexible.
The object adapter pattern also has the following advantages:
An object adapter can adapt multiple different adaptors to the same target, that is, the same adapter can adapt both the adaptor class and its subclasses to the target interface.
The disadvantages of the adapter-like pattern are as follows:
For languages that do not support multiple inheritance, such as Java and C#, only one adaptor class can be adapted at most at a time, and the target abstract class can only be an abstract class, not a concrete class. There are certain limitations in the application of the adaptor class and its subclasses can not be adapted to the target interface.
The disadvantages of the object adapter pattern are as follows:
In contrast to the class adapter pattern, it is not easy to replace the methods of the adapter class. If we must replace one or more methods of the adapter class, we have to make a subclass of the adapter class first, replace the method of the adapter class, and then take the subclass of the adapter class as the real adapter for adaptation, the implementation process is relatively complicated.
3.3 Structure and implementation of the pattern
The class adapter pattern can be realized by multiple inheritance. For example, C++ can define an adapter class to inherit both the business interface of the current system and the component interface that already exists in the existing component library. Java does not support multiple inheritance, but it is possible to define an adapter class that implements the business interfaces of the current system while inheriting components that already exist in an existing component library.
3.3.1 Structure of the mode
The object adapter pattern can be used to introduce components already implemented in an existing component library into an adapter class that also implements the business interface of the current system. Now let’s introduce their basic structure. Adapter patterns contain the following primary roles.
Queue Target Interface: The interface expected by the current system business, which can be abstract classes or interfaces. The Adaptee class: It is a component interface in an existing component library that is accessed and adapted. The Adapter class acts as a converter that converts an Adapter interface into a target interface by inheriting or referencing the Adapter’s objects, allowing customers to access the Adapter in the same format as the target interface.
The structure of the class adapter pattern is shown in Figure 1.
Figure 1 Shows the structure of the class 1 adapter pattern
The above is the first implementation of the Adapter pattern, Adapter Adapter inherits from Target and Adaptee class, Adapter class needs to rewrite the Target class Request function, do appropriate processing in the Request, call Adaptee class SepcificRequest. Finally, Target actually calls Adaptee’s SpecificRequest to fulfill Request adaptation. This is called a class adapter. The structure of the object adapter pattern is shown in Figure 2.
Figure 2 Structure diagram of the object adapter pattern
Class adapters have the following features:
1. Adapter directly inherits from the Adaptee class, so you can redefine the Adaptee method in Adapter class. 2. If an abstract method is added to the Adaptee, the Adapter must be changed accordingly, resulting in high coupling. 3. If there are other subclasses of Adaptee and you want to call methods of the other subclasses of Adaptee from Adapter, you cannot do so using class adapters.
Object adapters have the following features:
1. Sometimes you will find it difficult to construct an Adaptee object; 2. When a new abstract method is added to the Adaptee, the Adapter class can act correctly without any adjustment. 3. You can call methods of subclasses of the Adaptee class from an Adapter class in a polymorphic manner. Object adapters are recommended in many books because of their low coupling. In our actual project, the same is true, can use the object composition way, do not use the multiple inheritance way.
3.3.1 Implementation of the pattern
The code implementation of the class adapter:
class Target
{
public:
virtual void request(a)
{
cout << "Target::Request"<< std::endl; }};class Adaptee
{
public:
void specificalRequest(a)
{
cout << "Adaptee::SpecificRequest"<< std::endl; }};class Adapter : public Target, Adaptee
{
public:
void request(a)
{
Adaptee::specificalRequest();
}
};
Copy the code
Object adapter code implementation:
class Target
{
public:
Target() {}virtual ~Target() {}virtual void request(a)
{
cout << "Target::Request"<< endl; }};class Adaptee
{
public:
void specificalRequest(a)
{
cout << "Adaptee::SpecificRequest"<< endl; }};class Adapter : public Target
{
private:
Adaptee * adaptee;
public:
Adapter() : adaptee(nullptr)
{
adaptee = new Adaptee;
}
~Adapter()
{
if (nullptr! = adaptee)delete adaptee;
adaptee = nullptr;
}
void request(a)
{
adaptee->specificalRequest();
}
};
Copy the code
3.4 Application Scenarios of Mode
Sun company in 1996 opened the Java language database connection tool JDBC, JDBC makes the Java language program can connect with the database, and use SQL language to query and operate data. JDBC provides a common abstract interface for the client. The JDBC driver of a specific database engine (such as SQL Server, Oracle, and MySQL) is an adapter software between the JDBC interface and the database engine interface. Adapter software is required between the abstract JDBC interface and the various database engine apis, which are drivers for the various database engines.
3.5 Mode extension
Recognize the Default Adapter Pattern or the Default Adapter Pattern, when it is not necessary to implement all the methods provided by the interface, you can first design an abstract class implementation interface, and provide a Default implementation (empty method) for each method in the interface. Subclasses of the abstract class can optionally override some of the methods of the parent class to fulfill the requirement, which is applicable when an interface does not want to use all of its methods. Therefore, it is also called the single-interface adapter pattern.
Adapter mode (Adapter) can be extended to bidirectional Adapter mode. The bidirectional Adapter class can convert the Adapter interface into the target interface or the target interface into the Adapter interface. The structure diagram is shown in the figure.
Figure 3 Structure diagram of the bidirectional adapter pattern
3.6 summarize
The structural pattern describes how classes or objects are joined together to form a larger structure.
The adapter pattern is used to transform one interface into another that the customer wants, and the adapter pattern enables classes with incompatible interfaces to work together, alias wrapper. The adapter pattern can be used as either a class schema or an object schema.
The adapter pattern has four roles: the target abstract class defines the domain-specific interface that the customer wants to use; The adapter class can invoke another interface, acting as a converter to adapt the adapter and the abstract target class, which is the core of the adapter pattern. An adapter class is a role to be adapted. It defines an existing interface that needs to be adapted. Program against the target abstract class in the customer class, calling the business methods defined in the target abstract class.
In the class adapter pattern, the adapter class implements the interface of the target abstract class and inherits the adapter class, and calls the methods of the inherited adapter class in the implementation method of the target abstract class. In the object adapter pattern, the adapter class inherits the target abstract class and defines an object instance of the adapter class, which calls the corresponding business method of the adapter class in the inherited target abstract class method.
The main advantage of the adapter mode is to decouple the target class from the adapter class, which increases the transparency and reuse of the class. At the same time, the flexibility and scalability of the system are very good. It is very convenient to replace the adapter or add new adapters, which conforms to the “open and close principle”. The disadvantage of the class adapter pattern is that the adapter class cannot be adapted to multiple adaptor classes simultaneously in many programming languages, and the disadvantage of the object adapter pattern is that it is difficult to replace the methods of the adaptor class.
The adapter pattern applies when the system needs to use existing classes whose interfaces do not meet the needs of the system; You want to create a reusable class that works with classes that don’t have much to do with each other.