浅谈Java设计模式之适配器模式

369 阅读5分钟

概念

适配器模式是把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作

举个例子:

我们日常使用的笔记本电脑电源插头一般都是三相的,即除了阳极、阴极还有地级,而有些地方的电源插座只有两级,没有地级,这样将导致电源插座与笔记本电源插头不匹配而使得笔记本电脑无法使用。这时候就需要一个三相到两相的转换器(适配器)就能解决这个问题。

适配器模式的结构

适配器模式有类的适配器模式对象的适配器模式两种不同的形式。

模式所涉及的角色有:

  • 目标(Target)角色:所期待得到的接口
  • 源(Adapee)角色:现在需要适配的接口
  • 适配器(Adaper)角色:模式的核心,把源接口转换成目标接口

类的适配器模式

类的适配器模式继承Target类的同时实现Adapee类,实现Adapee类时调用Target类的方法。

举个例子:

Lightning接口:

public interface Lightning{
    
    void chargeByLightning();
}

该接口实现类:

public class LightningImpl implements Lightning{
    
    @Override
    void chargeByLightning(){
        System.out.println("通过lighting接口充电");
    }
}

TypeC线的接口:

public interface TypeC{
    
    void chargeByTypeC();
}

该接口实现类:

public class TypeCImpl implements TypeC{
    
    @Override
    void chargeByTypeC(){
        System.out.println("通过TypeC接口充电");
    }
}

适配器:

public class Adapter extends TypeCImpl implements Lightning{
    
    //适配器的核心
    @Override
    public void chargeByLightning(){
        chargeByTypeC();
    }
}

假设我有一部iPhoneX,现在要通过一条TypeC线来给我的手机充电,如下:

public class IPhoneX{
    
    public static void main(String[] args){
        //实例化适配器
        Adapter adapter = new Adapter();
        charge(adapter);
    }
    //注意参数是Lightning类型对象
    public static void charge(Lightning lightning){
		lightning.chargeByLightning();
    }
}

对象适配器模式

与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Target类,而是使用委派关系连接到Target类。

只需要修改下适配器即可:

public class Adapter implements Lightning{
    
    private TypeC typeC;
    
    public Adapter(){}
    
    public void setComputerOutlet(TypeC typeC){
        this.typeC = typeC;
    }
    
    public Adapter(TypeC typeC){
        this.typeC = typeC;
    }
    
    //适配器的核心
    @Override
    public void chargeByLightning(){
        typeC.chargeByTypeC();
    }
}

此时若要给iPhoneX充电,则需要在实例化适配器时传入一个TypeC类型的对象:

public class IPhoneX{
    
    public static void main(String[] args){
        //创建一个TypeC类型的对象
        TypeC typeC = new TypeCImpl();
        //实例化适配器,并将typeC对象作为参数传入构造函数中
        Adapter adapter = new Adapter(typeC);
        charge(adapter);
    }
    
    //注意参数是Lightning类型对象
    public static void charge(Lightning lightning){
		lightning.chargeByLightning();
    }
}

缺省适配器模式

缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,==它适用于不想使用一个接口中的所有方法的情况==,又称为单接口适配器模式。缺省适配器模式是适配器模式的一种变体,其应用也较为广泛。

适配器模式的应用

SpringMVC中的适配器模式

SpringMVC中的适配器模式主要用于执行目标Controller中的请求处理方法。

为什么需要在SpringMVC中使用适配器模式?

SpringMVC中Controller种类众多,不同类型的Controller通过不同的方法来对请求进行处理,如果不利用适配器模式的话,DispatcherServlet将直接获取对应类型的Controller,这时候需要我们自行来判断,像下面的代码一样:

if(mappedHandler.getHandler() instanceof MultiActionController){  
   ((MultiActionController)mappedHandler.getHandler()).handler(xxx);
}else if(mappedHandler.getHandler() instanceof XXX){  
    ...  
}else if(...){  
   ...  
} 

当每自定义一个Controller时就要在代码中加入一行if(mappedHandler.getHandler() instanceof XXX),这种形式将使得程序难以维护,也违反了设计模式中的开闭原则—对扩展开放,对修改关闭

先看看适配器接口 HandlerAdapter

public interface HandlerAdapter {
    //判断该适配器是否与某个Controller对应
    boolean supports(Object var1);

    //调用对应的Controller来进行处理请求操作
    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}

当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet会通过handler的类型找到对应的适配器,并将该适配器对象返回给用户,然后就可以统一通过适配器的handle()方法来调用Controller中用户处理请求的方法,可参见下面的源码:

public class DispatcherServlet extends FrameworkServlet {
    
    //用于存放Spring启动时注册的适配器对象
    private List<HandlerAdapter> handlerAdapters;
    
    //遍历handlerAdapters集合直至找到对应的适配器对象,否则抛出异常
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();
            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
        throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
 	
    //分发请求,请求需要找到匹配的适配器来处理
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        
        mappedHandler = this.getHandler(processedRequest);
        if (mappedHandler == null) {
            this.noHandlerFound(processedRequest, response);
            return;
        }
        
        //通过上面的方法获取到对应的适配器
        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
        //适配器执行handle()方法
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    	//省略..
    }

}

通过适配器模式我们可以将所有的Controller统一交给HandlerAdapter处理,免去了写大量的if-else语句对Controller进行判断,也更利于扩展新的Controller类型。


参考:

程杰:《大话设计模式》

孤落:Spring MVC中的适配器模式

ToughMind_:深入浅出设计模式(五):7.适配器模式