“This is the 7th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
1. What is the adapter pattern
Adapter Pattern converts the interface of a class into another interface representation expected by the client. The main purpose is compatibility, so that two classes that cannot work together due to interface mismatch can work together. This type of design pattern is structural.
For example: in life, our household voltage is generally 220V, and the charging voltage of our mobile phone is usually 5V. If the household voltage is directly connected to the battery and directly charged by the battery poles, the battery will be burned. At this point, our charger acts as an adapter, allowing the home power source to safely charge the phone battery
2. Principle of adapter mode
Composition of 2.1
The Adapter Pattern contains the following main roles:
- Target interface: The interface expected by the current system business. It can be a concrete class, abstract class, or interface.
- Adaptee class: An object that has an interface but is incompatible with the target interface.
- Adapter class: This is a converter that converts the Adapter interface into the target interface, allowing the customer to access the Adapter in the format of the target interface.
2.2 classification
Adapter patterns fall into three main categories
- Adapter-like pattern
- Object adapter pattern
- Interface adapter mode
Class 3 adapter patterns
3.1 the principle
The Adapter class inherits the Adaptee class and realizes the Target interface to complete the transformation from the Adaptee class to the Target interface. The following code takes charging a mobile phone as an example
3.2 class diagram
3.3 Core Code
Target interface:
Public interface IVoltage5V {public int output5V(); }Copy the code
Matches:
Public class Voltage220V {public int output220V() {int SRC = 220; System.out.println(" voltage =" + SRC + "); return src; }}Copy the code
The adapter:
Public class extends Voltage220 implements IVoltage5V {@override public int output5V() { Int srcV = output220V(); int dstV = srcV / 44 ; // return dstV; }}Copy the code
Client:
Public void bench (IVoltage5V IVoltage5V) {if(ivoltage5v.output5v () == 5) {public void bench (IVoltage5V IVoltage5V) {public void bench (IVoltage5V IVoltage5V) { System.out.println(" voltage is 5V, can charge ~~"); } else if (iVoltage5V.output5v () > 5) {system.out.println (" voltage > 5V, can't charge ~~"); }}}Copy the code
3.4 summarize
- Java is single inheritance, it must inherit the adapter class, so the target interface must be the interface class, there are certain limitations.
- Because the adapter inherits the adapter class, this can lead to additional methods or variables in the adapter that can directly access the adapter class, increasing the usage cost, but it can also override these methods.
4. Object adapter pattern
4.1 the principle
Similar to the above class adapter pattern, the difference is that the adapter is compatible with the target interface by holding instances of the adapted class (the adapter), such as in the form of member variables, and then implementing the target interface.
4.2 class diagram
4.3 Core Code
Only the adaptor and adapter references have changed here, so I only post this part of the code adaptor (no need to change either)
Public class Voltage220V {public int output220V() {int SRC = 220; System.out.println(" voltage =" + SRC + "); return src; }}Copy the code
The adapter
Public class VoltageAdapter implements IVoltage5V {private voltage220 voltage220; Public VoltageAdapter(Voltage220V Voltage220V) {this. Voltage220V = Voltage220V; } @Override public int output5V() { int dst = 0; if(null ! = voltage220V) { int src = voltage220V.output220V(); System.out.println(" use the object adapter for adaptation ~~"); dst = src / 44; System.out.println(" adaptation complete, output voltage =" + DST); } return dst; }}Copy the code
4.4 summarize
- Object adapters and class adapters are essentially similar in idea, but implemented differently. According to the principle of composite reuse, inheritance is replaced by composition, so it solves the limitation that the class adapter must inherit the adapter class, and the target interface class is not required to be an interface.
- Is a more flexible implementation approach than a class adapter.
5. Default adapter mode
5.1 the principle
Default adapter mode (interface adapter mode) : Default adaptation model provides the default implementation for an interface, is usually an abstract class as an intermediary to achieve interface all the way, the default is empty, (after jdk1.8, interfaces can be achieved, to a default method), so that we can extend from the default implementation, rather than having to extend from the original interface. This pattern is useful when there are too many methods defined in the original interface and most of them are not needed.
5.2 class diagram
5.3 Core Code
interface
public interface Interface4 {
public void m1();
public void m2();
public void m3();
public void m4();
}
Copy the code
An abstract class
Public abstract class AbsAdapter implements Interface4 {// Default implements public void m1() { } public void m2() { } public void m3() { } public void m4() { } }Copy the code
/ / a derived class
Public class AbsAdapterImpl extends AbsAdapter{@override public void m1() {system.out.println (" I implemented m1() method "); }}Copy the code
5.4 summarize
This pattern is used to selectively implement methods when there are too many methods defined in the original interface and most of them are not needed.
6 Application Cases
HandlerAdapter in SpringMvc.
public class DispatcherServlet extends FrameworkServlet { @Nullable private List<HandlerAdapter> handlerAdapters; Private void initHandlerAdapters(ApplicationContext context) {// Put all handlerAdapters in List<HandlerAdapter>. this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (! matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerAdapters); } } else { try { HandlerAdapter ha = (HandlerAdapter)context.getBean("handlerAdapter", HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException var3) { } } if (this.handlerAdapters == null) { this.handlerAdapters = this.getDefaultStrategies(context, HandlerAdapter.class); if (this.logger.isTraceEnabled()) { this.logger.trace("No HandlerAdapters declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties"); }}} / / by handlerMappings allocated the corresponding to each controller handle protected HandlerExecutionChain getHandler (it request) throws Exception { if (this.handlerMappings ! = null) { Iterator var2 = this.handlerMappings.iterator(); while(var2.hasNext()) { HandlerMapping mapping = (HandlerMapping)var2.next(); HandlerExecutionChain handler = mapping.getHandler(request); if (handler ! = null) { return handler; } } } return null; Protected HandlerAdapter getHandlerAdapter(Object Handler) throws ServletException {if (this.handlerAdapters ! = null) { Iterator var2 = this.handlerAdapters.iterator(); while(var2.hasNext()) { HandlerAdapter adapter = (HandlerAdapter)var2.next(); if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); } // Handle mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); }Copy the code
The implementation subclasses of HandlerAdapter enable each Controller to have a corresponding adapter implementation. Each Controller has a different implementation mode. When extending the Controller, you only need to add the implementation class of HandlerAdapter.