Overview
In software systems, due to changes in the application environment, we often need to put "some existing objects" in the new environment for application, however, the interfaces required by the new environment are not met by these existing objects. So how should we deal with this "Migration change "? How can we make good use of the existing object and meet the interfaces required by the new application environment? This is the Adapter mode described in this article.
Intention
Converts an interface of a class to another interface that the customer wants. The Adapter mode allows the classes that cannot work together due to incompatibility of interfaces to work together.
Structure chart
Figure 1 Structure of the Adapter Mode
Figure 2 Structure of the object's Adapter Mode
Examples in life
The adapter mode allows you to convert an interface of a class into another interface that the customer expects, so that classes that originally cannot work together due to interface incompatibility can work together. The wrench provides an example of an adapter. A hole is mounted on the spine and each side of the spine is of the same size. In the United States, the typical side lengths are 1/2 ''and 1/4 ''. Obviously, if no adapter is used, the 1/2 ''spine is not suitable for the 1/4'' hole. An adapter between 1/2 ''and 1/4'' has a 1/2 ''overcast slot to enclose a 1/2'' tooth, at the same time, there is a 1/4 Yang trough to card the 1/4 ''wrench.
Figure 3 examples of adapter objects using wrenches
Adapter mode description
The following uses the logging program as an example to describe the Adapter mode. Now there is a scenario where we want to use a third-party logging tool in software development. This logging tool supports database logging DatabaseLog and text file logging FileLog, the API provided to us is the Write () method, which is used as follows:
Log. write ("Logging Message! ");
When software system development is half done, this logging tool cannot be used for some reason, and another logging tool is required, it also supports database logging DatabaseLog and text file logging FileLog, except that it provides our API interface with the WriteLog () method. The usage is as follows:
Log. writeLog ("Logging Message! ");
The class structure of the logging tool is as follows:
Figure 4 Structure of Logging Tool
Its implementation code is as follows:
public abstract class LogAdaptee {public abstract void writeLog();}public class DatabaseLog extends LogAdaptee {public void writeLog() {System.out.println("Called WriteLog Method");}}public class FileLog extends LogAdaptee {public void writeLog() {System.out.println("Called WriteLog Method");}}
In the log record interface (called ILogTarget Interface) of the developed application, using the relevant role name in the Adapter mode), but using a large number of Write () methods, the program has passed the test, we cannot modify this interface. The Code is as follows:
public interface ILogTarget {void write();}
At this time, we may think of modifying the current log recording tool's API interface, but due to copyright and other reasons we can not modify its source code, this Adapter mode can be used. The following uses the Adapter mode to make the logging tool meet our current needs.
As mentioned above, the Adapter mode has two implementation structures. First, let's take a look at how class adapters are implemented. The only feasible method is to introduce a new type in the program to inherit the LogAdaptee class and implement the existing ILogTarget interface. Because LogAdaptee has two types, we naturally need to introduce two classes, DatabaseLogAdapter and FileLogAdapter, respectively.
Figure 5 structure chart after class adapter is introduced
The implementation code is as follows:
public class DatabaseLogAdapter extends DatabaseLog implements ILogTarget {public void write() {writeLog();}}public class FileLogAdapter extends FileLog implements ILogTarget {public void write() {writeLog();}}
Note that we have compiled an adaptation class for each logging method. Why cannot we compile an adaptation class for the abstract class LogAdapter? Although DatabaseLog and FileLog inherit from the abstract class LogAdapter at the same time, their specific implementations of writeLog () methods are different. The original behavior of a class can be retained only when the class is inherited.
Let's take a look at the call method of the client program:
public class Client {public static void main(String[] args) {ILogTarget dbLog = new DatabaseLogAdapter(); dbLog.write("Logging Database..."); ILogTarget fileLog = new FileLogAdapter(); fileLog.write("Logging File...");}}
Next, let's take a look at how to achieve our adaptation through the Object Adapter. Object adapters use object combinations instead of inheritance. The class structure is as follows:
Figure 6 structure chart after Object Adapter is introduced
The implementation code is as follows:
public class LogAdapter implements ILogTarget {private LogAdaptee adaptee; public LogAdapter(LogAdaptee adaptee) { this.adaptee = adaptee; } public void write(String log) { adaptee.writeLog(log); }}
Compared with the class adapter, the biggest difference is that the number of adapter classes is reduced, and you no longer need to create an adapter class for each specific logging method. At the same time, we can see that after the Object Adapter is introduced, the adapter class no longer depends on the specific DatabaseLog class and FileLog class, which makes it easier to implement loose coupling.
Let's take a look at the call method of the client program:
public class AdapterClient {public static void main(String[] args) {ILogTarget dbLog = new LogAdapter(new DatabaseLog()); dbLog.write("Logging Database..."); ILogTarget fileLog = new LogAdapter(new FileLog()); fileLog.write("Logging Database...");}}
Using the Adapter mode, we can reuse existing components. Compared with the above two adaptation modes, we can conclude that in the class adaptation mode, the adapter classes DatabaseLogAdapter and FileLogAdapter have all the behaviors of the parent class inherited by them, at the same time, it also has all the actions of the interface ILogTarget, which is in violation of the single responsibility principle of the class in the object-oriented design principle, and the Object Adapter is more in line with the object-oriented spirit, therefore, this method is not recommended in practical applications. From another perspective, let's look at the method of class adaptation. If the class we want to adapt to is written into the file and database at the same time when logging, we will use the Object Adapter to write it like this:
Implementation points
1. the Adapter mode is mainly used in scenarios where you want to reuse some existing classes, but the interfaces are inconsistent with the requirements of the reuse environment. It is useful in legacy code reuse and class library migration.
2. the Adapter mode has two forms of implementation structure: Object Adapter and class Adapter. However, the class Adapter adopts the "Multi-inheritance" implementation method, resulting in poor high coupling. Therefore, it is generally not recommended. The Object Adapter adopts the "Object combination" method, which is more compliant with the loose coupling spirit.
3. The implementation of the Adapter mode can be very flexible, so you do not have to stick to the two structures defined in GOF23. For example, the "existing object" in the Adapter mode can be used as a new interface method parameter to achieve adaptation.
4. the Adapter mode requires us to use the "interface-oriented programming" style as much as possible, so that we can easily adapt to it later. [The above points are referenced from MSDN WebCast]
Effect
For Class adapters:
1. Use a specific Adapter class to match Adaptee and Taget. The result is that when we want to match a class and all its subclasses, the class Adapter will not be competent.
2. Enable the Adapter to redefine partial behavior of Adaptee because the Adapter is a subclass of Adaptee.
3. Only one object is introduced, and no additional pointer is required to indirectly obtain Adaptee.
For object adapters:
1. Allow an Adapter to work with multiple Adaptee, that is, the Adaptee itself and all its subclasses (if any. The Adapter can also add functions to all Adaptee at a time.
2. making it difficult to redefine Adaptee behavior. This requires generating a subclass of Adaptee and making the Adapter reference this subclass instead of referencing the Adaptee itself.
Applicability
Use the adapter mode in the following situations:
1. The system needs to use an existing class, and such an interface does not meet the requirements of the system.
2. Create a reusable class for some classes that are not highly correlated with each other, including some classes that may be introduced in the future. These source classes do not necessarily have very complex interfaces.
3. (For object adapters) in the design, you need to change the interfaces of multiple existing sub-classes. If you use the class adapter mode, you need to create an adapter for each sub-class, which is not practical.
Summary
In short, by using the Adapter mode, you can fully enjoy the fun of migrating class libraries and reusing class libraries.
This document is from
. NET design mode (8): Adapter mode (Adapter Pattern)