1. 首页
  2. Java
  3. 设计模式

设计模式之适配器模式

适配器模式对于大家来说算是比较常见的用的比较多的模式了,主要功能是将类的接口转换为客户期望的另一个接口,使用适配器避免了由于接口不兼容而无法一起工作

在软件工程中,适配器模式是一种软件设计模式,它允许将现有类的接口用作另一个接口。它通常用于使现有类在不修改源代码的情况下与其他类一起工作。

一些现实生活中的例子

  • 存储卡上有一些图片,你需要把它们传输到电脑上面,为了传输它们,你需要某种与计算机端口兼容的适配器,以便可以将存储卡附加到计算机上
  • 电源适配器,一个三角插头不能连接到一个双孔插座上,它需要使用一个电源适配器,使它与双孔插座兼容
  • 另一个例子是翻译人员将一个人说的话翻译给另一个人

适配器模式有三种类型

类适配器实现适配器的接口,Adapter 类继承 Adaptee(被适配类),同时实现Target 接口,根据需要选择并创建任意一种符合需求的子类,来实现具体功能;

对象适配器不使用继承的方式而是使用直接关联,或者称为委托的方式;

接口适配器的核心在于中间适配器,用于实现默认的接口方法,由子类来决定使用哪些方法,让代码更加清晰整洁;

对象适配器

对象适配器的重点在于对象,是通过在直接包含Adaptee类来实现的,当需要调用特殊功能的时候直接使用Adapter中包含的那个Adaptee对象来调用特殊功能的方法即可。

一个只会使用划艇而不会操作渔船的船长

首先我们有接口 划艇(RowingBoat) 和 渔船(FishingBoat)

public interface RowingBoat {
  void row();
}
public class FishingBoat {
  private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
  public void sail() {
    LOGGER.info("The fishing boat is sailing");
  }
}

船长类实现 划艇(RowingBoat) 接口,使其能够移动

public class Captain implements RowingBoat {
  private RowingBoat rowingBoat;
  public Captain(RowingBoat rowingBoat) { this.rowingBoat = rowingBoat; }
  @Override
  public void row() { rowingBoat.row(); }
}

现在假设海盗来了,我们的船长需要逃跑,但是只有渔船可以使用,我们需要创建一个适配器来允许船长使用它的划艇技能操作渔船

public class FishingBoatAdapter implements RowingBoat {
  private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class);
  private FishingBoat boat;
  public FishingBoatAdapter() { boat = new FishingBoat(); }
  @Override
  public void row() { boat.sail(); }
}

现在 船长可以用渔船来躲避海盗了

Captain captain = new Captain(new FishingBoatAdapter());
captain.row();

类适配器示

类适配器的重点在于类,是通过构造一个继承Adaptee类来实现适配器的功能;  因为Java不支持多继承,所以只能通过接口的方法来实现多继承

// 标准接口
public interface Target {
    void play();
}
// 已实现play的功能
public class RowingBoat implements Target {
    private static final Logger LOGGER = LoggerFactory.getLogger(RowingBoat.class);
    @Override
    public void play() { LOGGER.info("滑动划艇...."); }
}
// 新的功能,摩托艇
public class Motorboat {
    private static final Logger LOGGER = LoggerFactory.getLogger(Motorboat.class);
    public void play() { LOGGER.info("驾驶摩托艇...."); }
}

适配器类,继承自Motorboat,并调用父类的play方法。配合标准接口实现功能转换

public class Adapter extends Motorboat implements Target{
    public Adapter() { }
    public void play() { super.play(); }
}
public class App {
    public static void main(String[] args) {
        Target target1 = new RowingBoat();
        target1.play();
        Target target = new Adapter();
        target.play();
    }
}

接口适配器示例

接口适配器应用场景,假设Target接口中有很多待实现的方法。

public interface Target {
    void motorboat();
    void rowingBoat();
    void fishingBoat();
    // ......
}

一般我们实现Target接口,默认需要实现接口中所有的方法。即使是我们用不到的。

使用接口适配器可以很好的解决这个问题。定义一个抽象接口类对Target接口的所有方法默认空实现

public abstract class Adapter implements Target {
    @Override
    public void motorboat() { }
    @Override
    public void rowingBoat() { }
    @Override
    public void fishingBoat() { }
    public void run(){
        motorboat();
        rowingBoat();
        fishingBoat();
    }
}

子类继承适配器类只需要覆盖需要实现的方法

public class Assembly1 extends Adapter {
    private static final Logger LOGGER = LoggerFactory.getLogger(Assembly1.class);
    @Override
    public void fishingBoat() { LOGGER.info("驾驶渔船..."); }
}

复写另外一组方法

public class Assembly2 extends Adapter {
    private static final Logger LOGGER = LoggerFactory.getLogger(Assembly2.class);
    @Override
    public void motorboat() { LOGGER.info("驾驶摩托艇...."); }
    @Override
    public void rowingBoat() { LOGGER.info("滑动划艇...."); }
}
public class App {
    public static void main(String[] args) {
        Adapter a1 = new Assembly1();
        a1.run();
        Adapter a2 = new Assembly2();
        a2.run();
    }
}

适配器模式的优缺点

优点

  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无需修改原有结构。
  • 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一适配者类可以在多个不同的系统中复用。
  • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便的更换适配器,也可以在不修改原有代码的基础上 增加新的适配器,完全复合开闭原则。

缺点

  • 一次最多只能适配一个适配者类,不能同时适配多个适配者。
  • 目标抽象类只能为接口,不能为类,其使用有一定的局限性。
  • 适配者类不能为最终类

适配器模式的实际应用

在SpringMVC中有一些不同类型的controller,并且实现方式也不一样,由于实现方式不一样调用的方式也不一样,如果直接调用controller方法,那么代码应该写成这样

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

这样假设如果我们增加一个HardController,就要在代码中加入一行 if (mappedHandler.getHandler() instanceof HardController) 这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 — 对扩展开放,对修改关闭。

因此Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替controller执行相应的方法。这样在扩展Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展了,真的是很精巧的做法!

废话不多说还是上代码吧,为了看得清楚,就自己实现一套代码来模拟springMVC, 直接贴Spring源码容易降低关注点。

// 定义一个Adapter接口  
public interface HandlerAdapter {  
    public boolean supports(Object handler);  
    public void handle(Object handler);  
}
// 以下是三种Controller实现  
public interface Controller {
}  
  
public class HttpController implements Controller{  
    public void doHttpHandler(){  
        System.out.println("http...");  
    }  
}  
  
public class SimpleController implements Controller{  
    public void doSimplerHandler(){  
        System.out.println("simple...");  
    }  
}  
  
public class AnnotationController implements Controller{  
    public void doAnnotationHandler(){  
        System.out.println("annotation...");  
    }  
}
// 下面编写适配器类  
public class SimpleHandlerAdapter implements HandlerAdapter {  
    public void handle(Object handler) {  
        ((SimpleController)handler).doSimplerHandler();  
    }  
    public boolean supports(Object handler) {  
        return (handler instanceof SimpleController);  
    }  
}  
  
public class HttpHandlerAdapter implements HandlerAdapter {  
    public void handle(Object handler) {  
        ((HttpController)handler).doHttpHandler();  
    }  
    public boolean supports(Object handler) {  
        return (handler instanceof HttpController);  
    }  
}  
 
public class AnnotationHandlerAdapter implements HandlerAdapter {  
    public void handle(Object handler) {  
        ((AnnotationController)handler).doAnnotationHandler();  
    }  
    public boolean supports(Object handler) {  
        return (handler instanceof AnnotationController);  
    }  
}
//模拟一个DispatcherServlet   
public class DispatchServlet {  
      
    public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();   
      
    public DispatchServlet(){  
        handlerAdapters.add(new AnnotationHandlerAdapter());  
        handlerAdapters.add(new HttpHandlerAdapter());  
        handlerAdapters.add(new SimpleHandlerAdapter());  
    }  

    public void doDispatch(){  
        // 此处模拟SpringMVC从request取handler的对象               
        // 不论实现何种Controller,适配器总能通过适配得到想要的结果  
        // HttpController controller = new HttpController();  
        // AnnotationController controller = new AnnotationController();  
        SimpleController controller = new SimpleController();  
        //得到对应适配器  
        HandlerAdapter adapter = getHandler(controller);  
        //通过适配器执行对应的controller对应方法  
        adapter.handle(controller);  
          
    }  
      
    public HandlerAdapter getHandler(Controller controller){  
        for(HandlerAdapter adapter: this.handlerAdapters){  
            if(adapter.supports(controller)){  
                return adapter;  
            }  
        }  
        return null;  
    }

    public static void main(String[] args){  
        new DispatchServlet().doDispatch();  
    }  
}  

使用了适配器模式的地方还有许多,比如Java中的Arrays.asList()、Collections.list() 等等。感兴趣的小伙伴可以了解一下。

发布者:Craig,转转请注明出处:https://www.goodlymoon.com/archives/1550.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

QR code