1、不同国家的插座是有区别的,如果我们去国外旅游,需要带上国外的插头转换器,来能兼容国外的插座;
2、手机的耳机孔有圆头和扁头,如果扁头的耳机孔想接圆头的耳机就需要一个耳机的转换器;
上述所说的转换器,其实就是适配器;它是用来做兼容的;
适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联(聚合)关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
Adaptee(适配者类—适配接口):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
以生活中充电器为例,充电器本身相当于适配者 (Adapter),220V 交流电相当于被适配者,我们的目标(target) 想把220V交流电转成5V直流电
要适配的类,即需要将220v电压转化为5v电压
//中国的电压220V
public class ChinaPower
{
private final Integer outPut=220;
public Integer getOutPut() {
return outPut;
}
}
适配器接口,只负责定义转化需要使用的业务逻辑方法,具体实现交由适配器类完成
//将电压转化为5v---适配接口
public interface TransTo5V
{
Integer transTo5V();
}
适配器类,继承了ChinaPower,并实现了适配器接口,负责实现讲220v电压转化为5v的具体业务逻辑代码实现
//适配器类---实现适配器接口
public class ChinaAdapter extends ChinaPower implements TransTo5V
{
//将220v电压转换为5v的
@Override
public Integer transTo5V()
{
//获得被适配类,即我们需要将220v电压转化为5v返回
Integer output=super.getOutPut();
//进行电压转换操作
return output/44;
}
}
Phone类,需要用到适配器进行兼容,这样才可以充电
//手机需要5v的电压进行充电
public class Phone
{
//通过适配器获得5v的电压
public void charging(ChinaAdapter chinaAdapter)
{
if(5==chinaAdapter.transTo5V())
{
System.out.println("得到5v,充电中...");
}
else
{
System.out.println("电压过高,手机压力过大");
}
}
}
充电测试
public class test
{
@Test
public void test()
{
Phone p=new Phone();
p.charging(new ChinaAdapter());
}
}
还是以上面的例子为例,这一次适配器类不再继承ChinaPower ,而是以聚合的方式来代替继承,符合设计模式中的"合成复用原则";java是单继承机制,这样可以保留对象继承权;
我们只需要修改适配器类即可:
//适配器类---实现适配器接口
public class ChinaAdapter implements TransTo5V
{
private ChinaPower chinaPower;
//通过构造器,完成赋值
public ChinaAdapter(ChinaPower chinaPower)
{
this.chinaPower=chinaPower;
}
//将220v电压转换为5v的
@Override
public Integer transTo5V()
{
//获得被适配类,即我们需要将220v电压转化为5v返回
Integer output=chinaPower.getOutPut();
//进行电压转换操作
return output/44;
}
}
测试
public class test
{
@Test
public void test()
{
Phone p=new Phone();
p.charging(new ChinaAdapter(new ChinaPower()));
}
}
对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承被适配者的局限性问题;
定义一个适配器接口:
public interface InterfaceTest {
public void m1();
public void m2();
public void m3();
public void m4();
}
抽象类 AbsAdapter 将 InterfaceTest 的方法进行默认实现,当子类需要使用适配器接口中的某个方法,而不是全部方法时,就可以通过继承抽象类,来完成对需要使用的特定方法重写操作即可,无需实现适配器接口里面的全部方法
public abstract class AbsAdapter implements InterfaceTest {
//默认实现
public void m1() {}
public void m2() {}
public void m3() {}
public void m4() {}
}
Client 调用接口,重写适配器抽象类方法
public class Client {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter() {
//只需要去覆盖我们 需要使用 接口方法
@Override
public void m1() {
System.out.println("使用了m1的方法");
}
};
absAdapter.m1();
}
}
一个顶层接口Power
public interface Power
{
Integer getOutPut();
}
分支一: 中国的220v电压
//中国的电压220V
public class ChinaPower implements Power
{
private final Integer outPut=220;
@Override
public Integer getOutPut() {
return outPut;
}
}
分支二: 日本的110v电压
//日本电压110v
public class JapenPower implements Power
{
private final Integer output=110;
@Override
public Integer getOutPut() {
return output;
}
}
失配器接口–DC5Adapter
//适配器接口
public interface DC5Adapter
{
boolean support(Power power);
Integer transTo5V(Power power);
}
适配器类—ChinaAdapter–只负责将220v电压进行转换的工作
//该适配器负责将中国的220v电压转化为5v的电压
public class ChinaAdapter implements DC5Adapter
{
//当前适配器只负责将220v电压转化为5v的功能实现
private static Integer voltage=220;
//判断当前适配器能否胜任传入power电压的转化职责
@Override
public boolean support(Power power)
{
if(power.getOutPut().equals(voltage))
return true;
return false;
}
//将220v电压转换为5v的
@Override
public Integer transTo5V(Power power)
{
//获得被适配类,即我们需要将220v电压转化为5v返回
Integer output=power.getOutPut();
//进行电压转换操作
return output/44;
}
}
适配器类—JapenAdapter–只负责将110v电压进行转换的工作
//该适配器负责将日本的110v电压转化为5v的电压
public class JapenAdapter implements DC5Adapter
{
//当前适配器只负责将110v电压转化为5v的功能实现
private static Integer voltage=110;
//判断是否支持将日本110v电压转化为5v电压的操作
@Override
public boolean support(Power power) {
if(power.getOutPut()==voltage)
return true;
return false;
}
//将110v电压转换为5v的
@Override
public Integer transTo5V(Power power)
{
//获得被适配类,即我们需要将110v电压转化为5v返回
Integer output=power.getOutPut();
//进行电压转换操作
return output/22;
}
}
//手机需要5v的电压进行充电
public class FindAdapter
{
//存放所有适配器的set集合
private static final Set<DC5Adapter> DC5Adapters=new HashSet<>();
//通过静态代码块进行初始化操作
static
{
DC5Adapters.add(new ChinaAdapter());
DC5Adapters.add(new JapenAdapter());
}
// 根据电压找合适的变压器
public DC5Adapter getPowerAdapter(Power power)
{
DC5Adapter dc5Adapter=null;
for(DC5Adapter da:DC5Adapters)
{
//如果遍历到当前电压合适的变压器就直接退出遍历
if(da.support(power))
{
dc5Adapter=da;
break;
}
}
//如果遍历完所有的变压器都没有找到合适的,就抛出异常
if(dc5Adapter==null)
{
throw new IllegalArgumentException("未能找到合适的变压器");
}
//返回找到的合适的变压器
return dc5Adapter;
}
}
public class test
{
@Test
public void test()
{
//找寻合适的变压器是第一步
FindAdapter fa=new FindAdapter();
//找寻可以将220v转化为5v的变压器,即适配器
DC5Adapter adapter = fa.getPowerAdapter(new ChinaPower());
//输出当前变压器转化之后的电压
System.out.println(adapter.transTo5V(new ChinaPower()));
}
}
具体来说,类适配器模式还有如下优点:
SpringMVM 中的 HandlerAdapter(上图的第4步), 就使用了适配器模式;
Spring MVC中的适配器模式主要用于执行目标 Controller
中的请求处理方法。
在Spring MVC中,DispatcherServlet
作为用户,HandlerAdapter
作为期望接口(适配器接口),具体的适配器实现类用于对目标类进行适配,Controller
作为需要适配的类。
为什么要在 Spring MVC 中使用适配器模式?Spring MVC 中的 Controller
种类众多,不同类型的 Controller
通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet
直接获取对应类型的 Controller
,需要的自行来判断,像下面这段代码一样:
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
这样假设如果我们增加一个 HardController
,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController)
,这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。
我们来看看源码,首先是适配器接口 HandlerAdapter
//适配器接口
public interface HandlerAdapter
{
//判断当前的controller请求是否能被当前的适配器类处理
boolean supports(Object var1);
//只有当支持处理当前请求后,才会执行下面的处理请求方法,返回一个ModelAndView对象
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
现该接口的适配器每一个 Controller
都有一个适配器与之对应,这样的话,每自定义一个 Controller
需要定义一个实现 HandlerAdapter
的适配器。
springmvc 中提供的 Controller
实现类有如下:
springmvc 中提供的 HandlerAdapter
实现类如下
HttpRequestHandlerAdapter 这个适配器代码如下:
//不同的适配器类实现不同的功能
//当前的HttpRequestHandlerAdapter 适配器类,只负责处理关于HttpRequest相关的请求
public class HttpRequestHandlerAdapter implements HandlerAdapter {
public HttpRequestHandlerAdapter() {
}
//判断当前的controller请求是否是HttpRequestHandler类型的
//当前适配器只支持处理当前类型的handler
public boolean supports(Object handler) {
return handler instanceof HttpRequestHandler;
}
//如果验证支持,会调用下面这个方法进行具体逻辑处理
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//先进行强制类型转换,转换为指定的handler类型,然后就可以调用该类型处理对应请求的方法了
//调用HttpRequestHandler的handleRequest处理对应的请求
((HttpRequestHandler)handler).handleRequest(request, response);
return null;
}
public long getLastModified(HttpServletRequest request, Object handler) {
return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
}
}
当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet
会通过 handler
的类型找到对应适配器,并将该适配器对象返回给用户,然后就可以统一通过适配器的 hanle()
方法来调用 Controller
中的用于处理请求的方法。
public class DispatcherServlet extends FrameworkServlet {
//用于存放所有HandlerAdapter适配器类的list集合
private List<HandlerAdapter> handlerAdapters;
//初始化handlerAdapters
private void initHandlerAdapters(ApplicationContext context) {
//..省略...
}
// 遍历所有的 HandlerAdapters,通过 supports 判断找到匹配的适配器
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;
}
}
}
// 分发请求,请求需要找到匹配的适配器来处理
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
//找到能处理当前processedRequest,即request请求的handler
mappedHandler = getHandler(processedRequest);
// 确定当前handler匹配的适配器类.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
ha.getLastModified(request, mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
// ...省略...
}
通过适配器模式我们将所有的 controller
统一交给 HandlerAdapter
处理,免去了写大量的 if-else
语句对 Controller
进行判断,也更利于扩展新的 Controller
类型。
使用 HandlerAdapter
的原因分析:
如果处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果直接调用 Controller
方法,就得不断使用 if else
来进行判断是哪一种子类然后执行。那么如果后面要扩展 Controller
,就得修改原来的代码,这样违背了 OCP
原则;
说明:
设计模式 8 - 适配器模式与springmvc源码分析 设计模式 | 适配器模式及典型应用 适配器模式(SpringMVC源码分析) 设计模式 | 适配器模式及典型应用