This article will take about 10 minutes to read

This article directory

  1. The introduction
  2. Applicable scenario
  3. Simple example coding
  4. Service Scenario Example
  5. Examples of framework application

The introduction

Xiao Ming and Xiao Hong are visiting England from China. They arrive at their hotel room

Small red 🥺 : my mobile phone how have no electricity, I want to charge, wrong, xiaoming you see foreign socket and our Chinese zha is not the same, my charger only have two heads, why this socket is three heads

Xiaoming 😎 : treasure, zha you Chinese mobile phone charger is cathode, anode, foreign many a pole, so we need a “adapter” can give our mobile phone charging

Xiao Hong 🤭 : Did you take it?

Xiaoming 🤨 : No

Xiao Ming Zu, the end of this article.

The example is not good, but it is simple and illustrates the problem

In popular understanding, an adapter is an intermediate role that plays a coordinating and compatible role

In our understanding of software development, the adapter pattern is a structural design pattern that transforms the interface of one class into another interface expected by the customer, so that classes with incompatible interfaces can work together

Its advantage in our software development is that it can improve the class transparency and reuse degree, existing class reuse does not need to change, to solve the problem of target class and existing class mismatch. And the target class and adapter class decouple, improve program extensibility

The disadvantages are also obvious, increasing the system code readable difficulty, such as calling interface A, in fact, has secretly called interface B

Applicable scenario

The scenario for which the adapter is applicable is usually an existing class whose methods do not match the requirements

Of course, if you are a strong partner, you can completely use your own interface. However, in general, with software maintenance, different products and manufacturers will cause similar functions but different interfaces. In this case, adapters are needed (in the following business scenarios, specific examples will be given).

A simple example

Adapter patterns are usually divided into object adaptation and class adaptation. The biggest difference is that one is implemented by inheritance of utility classes and the other is implemented by combination of utilities

Let’s start with an example of class adaptation

Suppose there is an existing MediaPlayer interface for audio playback

public interface MediaPlayer {
    void play(a);
}
Copy the code

The OldMediaPlayer only supported movies in Mp4 format

public class OldMediaPlayer implements MediaPlayer {
    @Override
    public void play(a) {
        System.out.println("Play it on Mp4"); }}Copy the code

Let’s have a test class

public class Test {
    public static void main(String[] args) {
        MediaPlayer mediaPlayer = newOldMediaPlayer(); mediaPlayer.play(); }}/ / outputIn the play the Mp4Copy the code

Ok, playing normally

Now the country is developing, we live a well-off life, want to watch avi format movies

Let’s start with a NewMediaPlayer that plays avi

public class NewMediaPlayer {
    public void adapteePlay(a){
        System.out.println("Play in Avi"); }}Copy the code

Then we need an Adapter Adapter to be compatible with the play interface action, so that our new player can use Play to play movies like the old player

public class Adapter extends NewMediaPlayer implements MediaPlayer {
    @Override
    public void play(a) {
        super.adapteePlay(); }}Copy the code

We inherit NewMediaPlayer and implement the MediaPlayer interface. In the Play method, we call the adapteePlay method of the parent NewMediaPlayer class to complete the transition. Wonderful!

Let’s go single

public class Test {
    public static void main(String[] args) {
        MediaPlayer mediaPlayer = new OldMediaPlayer();
        mediaPlayer.play();
        // The parent reference points to the subclass object
        MediaPlayer adapterMediaPlayer = newAdapter(); adapterMediaPlayer.play(); }}/ / outputPlay Avi in Mp4Copy the code

Everything is fine, the adapter is working perfectly 😎

Is there a better way to write this adapter? There is, of course, our object adaptation, which uses composition instead of inheritance, and both Effective Java and Ali’s Code Efficient recommend composition over inheritance

Next, transform the Adapter method

public class Adapter implements MediaPlayer {

    private NewMediaPlayer newMediaPlayer = new NewMediaPlayer();

    @Override
    public void play(a) { newMediaPlayer.adapteePlay(); }}Copy the code

The test class tests found the same output, salary + 1

Service Scenario Example

In the consumer finance scenario

As we step by step and grow bigger and stronger, we have connected with more and more trust companies and banks to make loans (collectively referred to as channels). Different channels need to do some different operations after lending, such as updating repayment plans, increasing limits and so on

So you can use an adapter to maintain that, and you can do that if you write an if statement

Suppose the existing A1 bank and B2 bank inform us of the success of the loan for example

public class A1LoanService {
    public void a1Loan(ChannelLoanContext context) {
        System.out.println("A1 channel lending success"); }}Copy the code
public class B2LoanService {
    public void b2Loan(ChannelLoanContext context) {
        System.out.println("B2 channel lending success"); }}Copy the code

Specify a unified lending interface, ChannelAdapterExecutor

public interface ChannelAdapterExecutor {
    boolean loan(ChannelLoanContext context);
}
Copy the code

A layer of channel actuators outside A1LoanService and B2LoanService are set to execute loan action in a unified manner

public class A1ChannelExecutor implements ChannelAdapterExecutor {

    private A1LoanService a1LoanService = new A1LoanService();

    @Override
    public boolean loan(ChannelLoanContext context) {
        a1LoanService.a1Loan(context);
        return true; }}Copy the code
public class B2ChannelExecutor implements ChannelAdapterExecutor {

    @Autowired
    private B2LoanService b2LoanService = new B2LoanService();

    @Override
    public boolean loan(ChannelLoanContext context) {
        b2LoanService.b2Loan(context);
        return true; }}Copy the code

Wonderful ah, later there are more channels to achieve this, fully meet the single responsibility.

Write a test class and see what happens

public class Test {
    public static void main(String[] args) {

        ChannelLoanContext a1ChannelLoanContext = new ChannelLoanContext();
        a1ChannelLoanContext.setChannelNo("A1");
        a1ChannelLoanContext.setLoanAmount(new BigDecimal("123"));
        ChannelAdapterExecutor a1ChannelAdapterExecutor = new A1ChannelExecutor();
        a1ChannelAdapterExecutor.loan(a1ChannelLoanContext);


        ChannelLoanContext b2ChannelLoanContext = new ChannelLoanContext();
        b2ChannelLoanContext.setChannelNo("A1");
        b2ChannelLoanContext.setLoanAmount(new BigDecimal("1234"));
        ChannelAdapterExecutor b2ChannelAdapterExecutor = newB2ChannelExecutor(); b2ChannelAdapterExecutor.loan(b2ChannelLoanContext); }}/ / outputA1 channel lending success B2 channel lending successCopy the code

Ok, the test also passed, the adapter did a good job

In actual business, we carry out unified field conversion through gateway or database configuration to meet our standardized requirements. And according to the different channel number, with the factory mode, route to the specific channel actuator

Examples of framework application

The HandlerAdapter in SpringMVC, which uses the adapter pattern, sounds pretty familiar

Take a look atHandlerAdapterThe implementation of the class

So why does SpringMvc need a HandlerAdapter

As we can see in the figure above, if we call the Controller method directly, we have to if else. If we need to expand the new processor later, we need to modify the original code, which violates our open and close principle

Suppose we did that…

if(mappedHandler.getHandler() instanceof SimpleController){
   ((SimpleController)mappedHandler.getHandler()).xxx
}else if(xx){
    ...
}else if(...). {... }Copy the code

If we add a controller, we need to write an if

Not very elegant 🤨

Ok, so let’s dig a little deeper, how does it work in Spring

Open the doDispatch method in the DispatcherServlet

If you don’t know what DispatcherServlet does, you can search it for yourself. You can continue to read it, but it simply returns a ModeAndView MV object

See line 940, which gets the handler (controller) based on the specific servlet request.

		mappedHandler = getHandler(processedRequest);
Copy the code

After obtaining the specific handler, mappedHandler, on line 947

	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
Copy the code

In getHandlerAdapter, loop through all handlerAdapters to find the appropriate Adapter

	/**
	 * Return the HandlerAdapter for this handler object.
	 * @param handler the handler object to find an adapter for
	 * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
	 */
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				returnha; }}throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}
Copy the code

Get the adapter of the specific Controller through the getHandlerAdapter

And then to 967 lines

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
Copy the code

Execute the method in the corresponding Controller according to the Adapter and return the ModelAndView

So far, we’ve seen how Spring handles the specific controllers it finds, but if you need an extended Controller, you just need to add an adapter class.


End of article, if this article has helped you, give it a like