This article will take about 10 minutes to read
This article directory
- The introduction
- Applicable scenario
- Simple example coding
- Service Scenario Example
- 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