1. definitions
Convert the interface of a class to another interface desired by the client, so that the classes that cannot work together due to incompatible interfaces can work together.
For example, the test class must use class A. now the test class needs to use some functions of class B on the basis of class A. at this time, there is an interface incompatibility problem. At this time, the adapter class can be used to integrate class B and class A. the adapter class has the functions of class B, but it can be declared as class A (specifically by inheriting the parent class or implementing the interface) when defining it, and externally expressed as an interface a, In this way, the test class does not need to modify the code that uses and defines class A.
Refer to the following code cases for details.
Adapter patterns are divided into class adapter patterns and object adapter patterns. The class adapter pattern implements the adapter class by inheritance, while the object adapter pattern implements the adapter class by composition or aggregation.
The former has a higher degree of coupling between classes than the latter, and requires users to understand the internal structure of relevant components of the existing component library, so the application is relatively less.
There is also an adapter mode: interface adapter mode. When you do not want to implement all the methods in an interface, you can create
Create an abstract class Adapter to implement all methods. At this point, we only need to inherit the abstract class.
2. structure of adapter mode
The adapter mode mainly includes the following main roles:
- Target interface: the interface expected by the current system business. It can be an abstract class or interface.
- Adapter class: it is the component interface in the existing component library that is accessed and adapted.
- Adapter class: it is a converter that converts the adapter interface into the target interface by inheriting or referencing the adapter object, so that customers can access the adapter according to the format of the target interface.
3. case code
Implementation method: define an adapter class to implement the business interface of the current system and inherit the existing components in the existing component library.
Case scenario
Take a simple example: suppose we have a mobile phone that needs to be charged, and the charging head of its charger is PlugA. However, due to some problems (self compensation), the power supply needs to be converted through the charging head PlugB before it can be used to charge the mobile phone. At this time, it is necessary to adapt PlugA to the power supply through PlugB. This scenario can be simulated through the adapter mode, and the power supply can be understood as the charging operation of the test class.
You can refer to the code for understanding.
Class 3.1 adapter mode
The class adapter mode is implemented by inheritance. The class diagram is as follows:
Of which:
- PlugA and PlugB are the interfaces of two plugs, and PlugAImpl and PlugBImpl are their implementation classes.
- MobilePhone class is a mobile phone class, in which there is a charging method. Its input only accepts PlugA type data, which means that it can only be charged through PlugA.
- PlugAAdapterPlugB class is an adapter class, which inherits PlugBImpl and implements PlugA interface. Therefore, it has the function of PlugBImpl, and its top-level type can be PlugA, which can be called by MobilePhone.
- Therefore, when using, we can directly use PlugAAdapterPlugB to create the plug object PlugA, which has been internally converted by PlugB.
1.PlugA and PlugB interfaces:
public interface PlugA { /* Interface of adapter class */ // Implementation method plug A charging void ChargeFromPlugA(); }
public interface PlugB { /* Target interface */ // charge void ChargeFromPlugB(); }
2. interface implementation class
public class PlugAImpl implements PlugA{ @Override public void ChargeFromPlugA() { System.out.println("With plug A charge"); } }
public class PlugBImpl implements PlugB{ @Override public void ChargeFromPlugB() { System.out.println("With plug B transformation"); } }
3. mobile phones
public class MobilePhone { //Mobile phones //Charging with A interface public void ChargeFromPlugA(PlugA plugA){ if(plugA == null){ throw new NullPointerException("plug is not null!"); } plugA.ChargeFromPlugA(); } }
4. adapter class
public class PlugAAdapterPlugB extends PlugBImpl implements PlugA{ @Override public void ChargeFromPlugA() { ChargeFromPlugB(); System.out.println("Using the plug A charge"); } }
5. testing
public class test { public static void main(String[] args) { System.out.println("===1.Phone charging without adapter==="); MobilePhone mobilePhone = new MobilePhone(); mobilePhone.ChargeFromPlugA(new PlugAImpl()); System.out.println("===2.Mobile phone use adapter B go to A charge[Class adapter mode]==="); PlugA plugA = new PlugAAdapterPlugB(); mobilePhone.ChargeFromPlugA(plugA); } }
result:
Thinking about class adapter pattern
- The class adapter pattern violates the composition Reuse Principle.
- The class adapter pattern can be used when the client class, that is, the PlugAImpl class in the above example, has an interface specification. If it does not, it cannot be implemented in this way.
3.2 object adapter mode
The object adapter mode implements the adapter class by combining or aggregating. That is to say, when we need the PlugBImpl class again, we can create the member variables of plugbtype in the adapter class by aggregating (of course, we can also create the variables of PlugBImpl type), and then pass the plugbinto the adapter class through the constructor. The adapter calls its methods, so that the adapter does not inherit the active parent class.
If there is no interface specification for the client class, PlugAImpl, when it is implemented, we can directly implement the adapter class by inheriting PlugAImpl, so as to avoid the problem that PlugAImpl inherits PlugBImpl in the class adapter mode
Class has no interface specification, which makes it impossible to implement the adapter.
The class diagram in this mode is as follows, so it will not be explained too much. In addition to the implementation relationship in the diagram, the adapter class PlugAAdapterPlugB_instance and PlugB are aggregation relationships, and MobilePhone and PlugA are dependency relationships.
The codes are as follows:
Other codes remain unchanged. Only the adapter class and test class can be modified.
Adapter class:
public class PlugAAdapterPlugB_instance implements PlugA{ private PlugB plugB; public PlugAAdapterPlugB_instance(PlugB plugB) { this.plugB = plugB; } @Override public void ChargeFromPlugA() { plugB.ChargeFromPlugB(); System.out.println("Using the plug A charge"); } }
Test class:
public class test { public static void main(String[] args) { System.out.println("===1.Phone charging without adapter==="); MobilePhone mobilePhone = new MobilePhone(); mobilePhone.ChargeFromPlugA(new PlugAImpl()); System.out.println("===2.Mobile phone use adapter B go to A charge[Class adapter mode]==="); PlugA plugA = new PlugAAdapterPlugB(); mobilePhone.ChargeFromPlugA(plugA); System.out.println("===3.Mobile phone use adapter B go to A charge[Object Adapter Pattern ]==="); PlugA plugA2 = new PlugAAdapterPlugB_instance(new PlugBImpl()); mobilePhone.ChargeFromPlugA(plugA); } }
result:
4. application scenarios
- The previously developed system has classes that meet the functional requirements of the new system, but their interfaces are inconsistent with those of the new system.
- Components provided by a third party are used, but the component interface definitions are different from those required by the user.