syshlang设计模式之结构型模式中的适配器模式(Adapter Pattern) | 枪口下的砚台

枪口下的砚台

所谓拥有,皆非束缚;所有过往,皆为序章。

0%

设计模式之结构型模式中的适配器模式(Adapter Pattern)

基本介绍

    生活中,我们接触到最多的适配器就是电源适配器,不管是电脑的电源适配器、手机电源适配器,还是其他的电子产品电源适配器,其主要目的都是将家用电源220V转换为电子产品需要的电压,这些适配器的接口有两孔的也有三孔的,实质就是把一个转接头或者电压变换成另外所需要的插口或者电压。拿到编程里面来说,就是将一个类的接口转换成客户端所希望的另外一个接口,这样使得原本不能一起工作的那些类(接口不兼容,方法不匹配等)可以一起工作,主要目的就是处理兼容性。

适配器模式分类

    适配器模式主要分为三类:类适配器、对象适配器、接口适配器。类适配器和对象适配器在实现上有些差别,而接口适配器则差别较大。

类适配器模式

    类适配器实现的原理主要是通过继承。适配器类(Adapter)通过继承需要被适配的类(Source),实现需要得到的类(Destination)接口,从而完成Adapter到Destination的适配。

日常举例

手机充电的例子,通过手机充电器(Adapter)完成220V电源(Source)到 5V电压(Destination)的转换

1
2
3
4
5
public class Source220V {
public int outVoltage220v(){
return 220;
}
}
1
2
3
4
5
6
7
public interface Destination5V {
/**
*
* @return
*/
int outVoltage5v();
}
1
2
3
4
5
6
7
public class VoltageAdapter extends Source220V implements Destination5V{
@Override
public int outVoltage5v() {
int voltage220v = outVoltage220v();
return voltage220v/44;
}
}
1
2
3
4
5
6
public class Client {
public static void main(String[] args) {
VoltageAdapter voltageAdapter = new VoltageAdapter();
voltageAdapter.outVoltage5v();
}
}

对象适配器模式

    对象配器实现的原理主要是通过组合或聚合。在《设计模式七大原则之合成复用原则(Composite Reuse Principle)》中,合成复用原则指出尽量使用合成/聚合,尽量不要使用类继承。对象适配器模式思路和类适配器模式基本相同,只不过将适配器类(Adapter)做修改,不再继承需要被适配的类(Source),而是直接持有需要被适配的类(Source),实现需要得到的类(Destination)接口,从而完成Adapter到Destination的适配。

日常举例

仍然是手机充电的例子,只需要修改适配器类(Adapter),如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class VoltageAdapter  implements Destination5V{
/**
* 直接持有需要被适配的类(Source)
*/
private Source220V source220V;

public VoltageAdapter(Source220V source220V) {
this.source220V = source220V;
}

@Override
public int outVoltage5v() {
int voltage220v = source220V.outVoltage220v();
return voltage220v/44;
}
}
1
2
3
4
5
6
public class Client {
public static void main(String[] args) {
VoltageAdapter voltageAdapter = new VoltageAdapter(new Source220V());
voltageAdapter.outVoltage5v();
}
}

接口适配器模式

    接口配器实现的原理主要是通过抽象类来实现适配。接口适配器模式的核心思路是,当不需要全部实现接口的方法时,可以先设计一个抽象的类实现接口,并为接口中的每个方法提供一个默认的实现,对于该抽象类的子类就可以有选择的覆盖父类的某些方法,从而达到适配的目的。因此,该模式也被称为缺省适配器模式或是默认适配器模式(Default Adapter Pattern)。

日常举例

仍然是手机充电的例子,代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface Destination {
/**
*
* @return
*/
int outVoltage5v();

/**
* @return
*/
int outVoltage10v();

/**
* @return
*/
int outVoltage36v();

/**
* @return
*/
int outVoltage220v();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public  abstract class AbstractVoltageAdapter implements Destination{
@Override
public int outVoltage5v() {
return 5;
}

@Override
public int outVoltage10v() {
return 10;
}

@Override
public int outVoltage36v() {
return 36;
}

@Override
public int outVoltage220v() {
return 220;
}
}
1
2
3
4
5
6
7
public class VoltageAdapter extends AbstractVoltageAdapter{
@Override
public int outVoltage5v() {
int voltage220v = outVoltage220v();
return voltage220v/44;
}
}
1
2
3
4
5
6
public class Client {
public static void main(String[] args) {
VoltageAdapter voltageAdapter = new VoltageAdapter();
voltageAdapter.outVoltage5v();
}
}

适配器模式在Spring框架中的应用

对于SpringMVC有一个很重要的servlet,它有一个方法:
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
截取部分源码如下:
源码:org.springframework.web.servlet.DispatcherServlet(片段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class DispatcherServlet extends FrameworkServlet {
...
/**
* 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)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
...
}

源码:org.springframework.web.servlet.HandlerAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public interface HandlerAdapter {

/**
* Given a handler instance, return whether or not this {@code HandlerAdapter}
* can support it. Typical HandlerAdapters will base the decision on the handler
* type. HandlerAdapters will usually only support one handler type each.
* <p>A typical implementation:
* <p>{@code
* return (handler instanceof MyHandler);
* }
* @param handler handler object to check
* @return whether or not this object can use the given handler
*/
boolean supports(Object handler);

/**
* Use the given handler to handle this request.
* The workflow that is required may vary widely.
* @param request current HTTP request
* @param response current HTTP response
* @param handler handler to use. This object must have previously been passed
* to the {@code supports} method of this interface, which must have
* returned {@code true}.
* @throws Exception in case of errors
* @return ModelAndView object with the name of the view and the required
* model data, or {@code null} if the request has been handled directly
*/
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

/**
* Same contract as for HttpServlet's {@code getLastModified} method.
* Can simply return -1 if there's no support in the handler class.
* @param request current HTTP request
* @param handler handler to use
* @return the lastModified value for the given handler
* @see javax.servlet.http.HttpServlet#getLastModified
* @see org.springframework.web.servlet.mvc.LastModified#getLastModified
*/
long getLastModified(HttpServletRequest request, Object handler);

}

再看HandlerAdapter的继承关系图:

分析: 从上面的源码片段及HandlerAdapter的继承关系图,可以看出Spring定义了一个适配接口HandlerAdapter,而其实现子类使得每一种Controller都有一种对应的适配器实现类,扩展Controller时只需增加对应的适配器实现类就可以了。

附:本次演示的项目地址
https://github.com/syshlang/java-design-patterns

------------- The End -------------
  • 本文作者: syshlang
  • 本文链接: https://www.syshlang.com/fbfea71e/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!