“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:

  1. Target interface: The interface expected by the current system business. It can be a concrete class, abstract class, or interface.
  2. Adaptee class: An object that has an interface but is incompatible with the target interface.
  3. 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

  1. Adapter-like pattern
  2. Object adapter pattern
  3. 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

  1. Java is single inheritance, it must inherit the adapter class, so the target interface must be the interface class, there are certain limitations.
  2. 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

  1. 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.
  2. 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.