The famous “Gang of Four” design pattern describes the adapter pattern as transforming the interface of one class into another interface that the customer wants. The Adapter pattern enables classes to work together that would otherwise not work together due to interface incompatibilities. – the Gang of Four
This article introduces the Adapter Pattern. In real life, there are many examples similar to adapters. For example, some foreign voltage is 110V and the domestic voltage is 220V, so the electrical appliances brought back from abroad need a transformer to normal use.
Ultimately, the adapter pattern is about fusing two previously incompatible classes together, sort of like a glue that takes different things and makes them work together through a transformation. When faced with interacting with two completely unrelated classes, the first solution is to modify the interfaces of each class, but what if you cannot modify the source code or for some other reason cannot change the interfaces? In this case, we often use a Single Adapter, creating a glue interface between the two interfaces to make the previously incompatible classes compatible without changing the code of the original two modules, conforming to the open closed principle. There are three types of adapter patterns: class adapter, object adapter, and interface adapter. The first two are slightly different in implementation and serve the same purpose. The third interface adapter is more different.
The class adapter
Target: The Target role, that is, the expected interface. Since we are talking about the class adapter pattern, the Target cannot be a class. Adaptee: the interface that needs to be adapted now; Adapter: Adapter role. The Adapter converts the source interface into the target interface, so this role must be a concrete class.
Examples Take the foreign and domestic voltages mentioned above, starting with two voltage classes: Java
Public interface InlandVoltage {String getInlandType(); }Copy the code
Public class implements InlandVoltageImpl {@override public StringgetInlandType() {
// TODO Auto-generated method stub
return "Inland Voltage is 220V"; }}Copy the code
Public class ForeignVoltage {public StringgetForeignType() {
return "Foreign Voltage is 110V"; }}Copy the code
Then there are the adapters, which bring the domestic voltage down to the foreign voltage standard
public class IAdapter extends ForeignVoltage implements InlandVoltage {
@Override
public String getInlandType() {
// TODO Auto-generated method stub
returngetForeignType(); }}Copy the code
Object adapter
Like the adaptor pattern for classes, the adaptor pattern for objects converts the API of the adapted class into the API of the target class. Unlike the Adaptee pattern for classes, the Adaptee pattern for objects uses delegate relationships instead of inheritance relationships to connect to Adaptee classes.
The sample
java
Public interface InlandVoltage {String getInlandType(); }Copy the code
Public class implements InlandVoltageImpl {@override public StringgetInlandType() {
// TODO Auto-generated method stub
return "Inland Voltage is 220V"; }}Copy the code
Public class ForeignVoltage {public StringgetForeignType() {
return "Foreign Voltage is 110V"; }}Copy the code
public class IAdapter implements InlandVoltage{ private ForeignVoltage faForeignVoltage; public IAdapter(ForeignVoltage faForeignVoltage) { this.faForeignVoltage = faForeignVoltage; } // Override public StringgetInlandType() {
// TODO Auto-generated method stub
returnfaForeignVoltage.getForeignType(); }}Copy the code
Usage scenarios of adapters
- The system needs to use the existing class, and the interface of this class does not meet the needs of the system, that is, the interface is incompatible;
- You want to create a reusable class that works with classes that have little to do with each other, including classes that may be introduced in the future.
- A unified output interface is required, and the type of input is unpredictable.
Choice of class adapter versus object adapter
- For class adapters, because the adapter directly inherits from Adaptee, the adapter cannot work with subclasses of Adaptee, because inheritance is static, when the adapter inherits from Adaptee, it is not possible to deal with subclasses of Adaptee. For object adapters, one adapter can adapt many different sources to the same target. In other words, the same adapter can adapt both the source class and its subclasses to the target interface. Because the object adapter is a combination of objects, it doesn’t matter if the object is subclassed as long as the object type is correct.
- For class adapters, the adapter can redefine part of the Adaptee’s behavior in the same way that a subclass overrides part of its parent class’s implementation methods. For object adapters, it is difficult to redefine the behavior of Adaptee, in which case you need to define subclasses of Adaptee to implement the redefinition, and then let the adapter combine subclasses. Although redefining Adaptee’s behavior is difficult, it is convenient to add new behaviors that can be applied to all sources simultaneously.
- For class adapters, only one object is introduced and no additional references are required to get the Adaptee indirectly. For object adapters, additional references are required to get the Adaptee indirectly.
It is recommended to use the object adapter implementation as much as possible, with more composition/aggregation and less inheritance. Of course, specific problems specific analysis, according to the need to choose the implementation, the most suitable is the best.
Advantages and disadvantages of adapters:
- The adapter pattern is also a wrapper pattern that has the same wrapper function as the decorator pattern, and the object adapter pattern also has the meaning of delegation. In general, the adapter mode is a compensation mode, intended for use when the system is extended or modified later.
Disadvantages:
- Too much use of adapters will make the system very messy, not easy to grasp as a whole. For example, clearly see is called A interface, in fact, internal adaptation into the IMPLEMENTATION of THE B interface, A system if too much of this situation, is tantamount to A disaster. So if you don’t have to, you can skip the adapter and refactor your system directly.
Interface adapter (default adapter) when there is an interface, which defines the N many methods, but now we just want to use one of these by several methods, if we implement the interface directly, so we want to implement all methods, even if we just don’t need to approach for empty (only a pair of curly braces, Does not make specific method implementation) can also cause the class become bloated, call is not convenient, then we can use an abstract class as middleware, namely, adapter, this abstract class is used to implement interfaces, and all the methods in the abstract class is empty, so we create an abstract class derived classes, and rewriting of the several methods we need to use it. Java defines an animal interface with a number of methods.
Public interface Animal {public void sleep (); Public void (); Public void eat (); Public void ran (); Public void swim (); }Copy the code
Interface adapter
Public class adapter implements Animal{@override public void implements Animal () {} @override public void implements Animal () {} @override public void implements Animal () {} @override public void implements Animal () {} @override public void implements Animal () {} @override public } @override public void run () {} @override public void swim () {}}Copy the code
Implementing a dog-like approach only implements part of the method
Public class Dog extends IAdapter{@override public void sleep () {system.out.println ("Sleep"); } @override public void eat () {system.out.println ("Bone eating"); }}Copy the code
The default adaptation mode is a “banal” adapter mode. The intent of the adapter pattern is to change the source interface so that the target interface is compatible. The purpose of default adaptation is slightly different, providing a mediocre implementation to facilitate the creation of a non-mediocre adapter class.
These empty methods are wasteful and sometimes confusing. Unless you have seen the code for these empty methods, a programmer might assume that they are not empty. Even if he knows that some of these methods are empty, he may not know which methods are empty and which are not, unless he has seen the source code or documentation for those methods.
The default adaptation mode can handle this situation well. The interface can be implemented by designing an abstract adapter class that provides an empty method for each method required by the interface. This abstract class can save its concrete subclasses from being forced to implement empty methods