“This is the 22nd day of my participation in the August Genwen Challenge.More challenges in August”

What is the adapter pattern?

The function of Adapter Pattern is to change the interface of a class into another interface expected by the client, so that two classes that cannot work together due to interface mismatch can work together. It belongs to the structural design Pattern.

That is to say, the current system there are two interfaces of A and B, customer support only visit to A interface, but the current system is not A interface object, have B interface object, the customer can’t identify B interface, so you need to pass an adapter C, will B interface content into A interface, so that the customer can get from A interface to get B interface.

In software development, virtually any problem can be solved by adding an intermediate layer. The adapter pattern is really an intermediate layer. In summary, the adapter pattern acts as a transformation/delegate, transforming one interface into another that meets the requirements.

The application scenarios of the adapter pattern

Provides a converter (adapter) that converts an object that currently exists on the system into an interface object that is more accessible to clients. Adapters are used in the following business scenarios:

  1. An existing class whose methods and requirements do not match (the method results are the same or similar).
  2. Adapter mode is not a design mode considered in the software design stage. It is a solution in the case of similar functions but different interfaces caused by different products and manufacturers along with software maintenance. It is a bit of a feeling of late repair.

Life application scenarios, such as power conversion head, mobile phone conversion head, display conversion head.

Roles involved in the adapter pattern

The adapter pattern generally contains three roles:

  1. Target: That is, the desired interface.
  2. Source role (Adaptee) : An interface instance that exists in the system, whose content meets customer requirements (need to be transformed), but whose interfaces do not match.
  3. Adapter: Class instance that converts the source role (Adaptee) to the Target role (Target).

The relationships between the roles in the adapter pattern are as follows:

Assume that the current system, the client needs to access the Target interface, but the Target interface does not meet the requirements of an instance, and the Adaptee instance meets the requirements, but the client can not directly use Adaptee (interface incompatible), therefore, we need an Adapter to transfer, Enable Adaptee to convert to Target interface form.

Four, three forms of adapter pattern

The adapter pattern comes in three forms: class adapter, object adapter, and interface adapter.

1. Class adapter

The principle of the class Adapter is to implement the Adapter function through inheritance, specific approach: let the Adapter implement Target interface, and inherit Adaptee, so that the Adapter has Target and Adaptee features, you can convert the two.

In China, civil electricity is 220V AC, but the lithium batteries used in our mobile phones use 5V DIRECT current. Therefore, we need to use the power adapter to change the phone when charging it. Below we have the code to restore the life scene, create the Adaptee role, need to be converted object AC220 class, represents 220V AC.

public class AC220V {
    public int outputAC220V(a) {
        int output = 220;
        System.out.println("Output voltage" + output + "V");
        returnoutput; }}Copy the code

Create Target role DC5 interface, representing the standard of 5V DC:

public interface DC5 {
    int outputDC5V(a);
}
Copy the code

Create an Adapter role that converts 220V current to 5V current.

public class PowerAdapter extends AC220V implements DC5 {
    @Override
    public int outputDC5V(a) {
        int outputAC220V = super.outputAC220V();
        int adapterOutput = outputAC220V / 44;
        System.out.println("Enter AC using Adapter" + outputAC220V + "V, output DC:" + adapterOutput + "V");
        returnadapterOutput; }}Copy the code

Client test code:

public class Test {
    public static void main(String[] args) {
        DC5 adapter = newPowerAdapter(); adapter.outputDC5V(); }}Copy the code

Output result:

In the above example, the two are compatible by adding a PowerAdapter PowerAdapter.

2. Object adapter

The principle of the object Adapter is to implement the Adapter function through composition: let the Adapter implement the Target interface, then hold the Adaptee instance internally, and then transform the Adaptee within the method specified by the Target interface.

The code only needs to change the Adapter implementation, otherwise the same as the Adapter:

public class PowerAdapter implements DC5 {

    private AC220V ac220V;

    public PowerAdapter(AC220V ac220V) {
        this.ac220V = ac220V;
    }

    @Override
    public int outputDC5V(a) {
        int outputAC220V = this.ac220V.outputAC220V();
        int adapterOutput = outputAC220V / 44;
        System.out.println("Enter AC using Adapter" + outputAC220V + "V, output DC:" + adapterOutput + "V");
        returnadapterOutput; }}Copy the code

3. Interface adapter

The concern of interface adapter is different from that of class adapter and object adapter. Class adapter and object adapter focus on converting an existing role (Adaptee) of the system into the content required by the Target interface. The use scenario of interface adapter is to solve the interface method too much. Then the class will have many empty implementation methods, the class will appear bloated. Using an interface adapter at this point allows us to implement only the interface methods we need, making the goal clearer.

The main principle of an interface adapter is to implement an interface using abstract classes and implement many methods of the interface. Create Target role DC class:

public interface DC {
    int output5V(a);

    int output12V(a);

    int output24V(a);

    int output36V(a);
}
Copy the code

Create the Adaptee role AC220V class:

public class AC220V {
    public int outputAC220V(a) {
        int output = 220;
        System.out.println("Output voltage" + output + "V");
        returnoutput; }}Copy the code

Create Adapter role PowerAdapter class:

public class PowerAdapter implements DC {

    private AC220V ac220V;

    public PowerAdapter(AC220V ac220V) {
        this.ac220V = ac220V;
    }

    @Override
    public int output5V(a) {
        int outputAC220V = this.ac220V.outputAC220V();
        int adapterOutput = outputAC220V / 44;
        System.out.println("Enter AC using Adapter" + outputAC220V + "V, output DC:" + adapterOutput + "V");
        return adapterOutput;
    }

    @Override
    public int output12V(a) {
        return 0;
    }

    @Override
    public int output24V(a) {
        return 0;
    }

    @Override
    public int output36V(a) {
        return 0; }}Copy the code

Client code:

public class Test {
    public static void main(String[] args) {
        DC adapter = new PowerAdapter(newAC220V()); adapter.output5V(); }}Copy the code

Five, adapter mode in the source code

The adapter pattern is also widely used in Spring, for example: The AdvisorAdapter class in Spring AOP, it has three implementation class MethodBeforeAdviceAdapter, AfterReturningAdviceAdapter and ThrowsAdviceAdapter, Take a look at the Source code for AdvisorAdapter:

public interface AdvisorAdapter {

  /**
   * Does this adapter understand this advice object? Is it valid to
   * invoke the {@code getInterceptors} method with an Advisor that
   * contains this advice as an argument?
   * @param advice an Advice such as a BeforeAdvice
   * @return whether this adapter understands the given advice object
   * @see #getInterceptor(org.springframework.aop.Advisor)
   * @see org.springframework.aop.BeforeAdvice
   */
  boolean supportsAdvice(Advice advice);

  /**
   * Return an AOP Alliance MethodInterceptor exposing the behavior of
   * the given advice to an interception-based AOP framework.
   * <p>Don't worry about any Pointcut contained in the Advisor;
   * the AOP framework will take care of checking the pointcut.
   * @param advisor the Advisor. The supportsAdvice() method must have
   * returned true on this object
   * @return an AOP Alliance interceptor for this Advisor. There's
   * no need to cache instances for efficiency, as the AOP framework
   * caches advice chains.
   */
  MethodInterceptor getInterceptor(Advisor advisor);

}
Copy the code

Then look at MethodBeforeAdviceAdapter class:

class MethodBeforeAdviceAdapter implements AdvisorAdapter.Serializable {

  @Override
  public boolean supportsAdvice(Advice advice) {
    return (advice instanceof MethodBeforeAdvice);
  }

  @Override
  public MethodInterceptor getInterceptor(Advisor advisor) {
    MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
    return newMethodBeforeAdviceInterceptor(advice); }}Copy the code

The HandlerAdapter class in Spring MVC has several subclasses:

The key code for the adapter call is in the doDispatch() method of the DispatcherServlet.

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try{ processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest ! = request);// Determine handler for the current request.
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
          noHandlerFound(processedRequest, response);
          return;
        }

        // Determine handler adapter for the current request.
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // Process last-modified header, if supported by the handler.
        String method = request.getMethod();
        boolean isGet = "GET".equals(method);
        if (isGet || "HEAD".equals(method)) {
          long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
          if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
            return; }}if(! mappedHandler.applyPreHandle(processedRequest, response)) {return;
        }

        // Actually invoke the handler.
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        if (asyncManager.isConcurrentHandlingStarted()) {
          return;
        }

        applyDefaultViewName(processedRequest, mv);
        mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
        dispatchException = ex;
      }
      catch (Throwable err) {
        // As of 4.3, we're processing Errors thrown from handler methods as well,
        // making them available for @ExceptionHandler methods and other scenarios.
        dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
          new NestedServletException("Handler processing failed", err));
    }
    finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
        // Instead of postHandle and afterCompletion
        if(mappedHandler ! =null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); }}else {
        // Clean up any resources used by a multipart request.
        if(multipartRequestParsed) { cleanupMultipart(processedRequest); }}}}Copy the code

The getHandlerAdapter() method is called in the doDispatch() method.

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters ! =null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
        if (adapter.supports(handler)) {
          returnadapter; }}}throw new ServletException("No adapter for handler [" + handler +
        "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
  }
Copy the code

Advantages and disadvantages of the adapter pattern

Advantages:

  1. Can improve class transparency and reuse, existing classes reuse but need not change.
  2. Decouple the target class from the adapter class to improve program extensibility.
  3. It complies with the on/off principle in many business scenarios.

Disadvantages:

  1. The adapter pattern authoring process needs to be considered comprehensively and can add complexity to the system.
  2. Increase code reading difficulty, reduce code readability, excessive use of adapters will make the system code messy.

7. Friendship links

Design Patterns – Factory Patterns learning tour

Design Patterns – a learning tour of singleton patterns

Design Patterns – A learning journey of prototyping patterns

Design Patterns – Builder patterns learning tour

Design Patterns – Agent patterns learning tour

Design Patterns – A learning tour of facade patterns

Design Patterns – A learning tour of decorator patterns

Design Patterns – Enjoy yuan patterns learning journey

Design Patterns – A learning journey of composite patterns

Welcome to follow the wechat public account (MarkZoe) to learn from and communicate with each other.