设计模式(8):适配器模式

「尺有所短,寸有所长;不忘初心,方得始终。」

一、适配器模式是什么

适配器模式属于结构型模式,是作为两个不兼容的接口之间的桥梁,结合了两个独立接口的功能。

设计模式(8):适配器模式
  • 「主要作用」:将原本接口不兼容的类转换成客户端所需要的类,以达到接口的兼容性。

  • 「主要解决」:不同环境或系统中,接口实现不通而无法兼容的情况。

  • 「实现核心」:继承或依赖(继承或依赖已有的对象,实现想要的目标接口)。

  • 「4适配器模式分为种类型」

    「由于类适配器在Java中无法实现,单接口适配器,双向适配器在实际中很少使用,故本文主要讲解对象适配器。」

    • 「对象适配器模式」

    • 「类适配器模式」

      适配器同时继承两个对象的接口,仅能在支持多重继承的编程语言中实现(C++)

    • 「单接口适配器模式(缺省适配器模式)」

    • 「双向适配器模式」

二、适配器模式的适用场景

  • 「适用场景」

    • 系统需要使用某个类,而该类的接口与客户端的代码不兼容时,可以使用适配器类。

      创建一个中间类, 在中间类中转换第三方类与客户端之间。

    • 当有一些彼此之间没有太大关联的类,又需要他们在一起使用的时候,可以使用适配器类。

    • 通过接口转换,将一个类插入另一个类系中

  • 「案例」

    • 手机充电线与插座之间的「充电头」
    • 欧美的三孔插座与国标三孔插座之间的「转换器」

三、适配器模式结构(对象适配器)

  • 「客户端接口」 (Client Interface) :目标角色,描述了其他类与客户端代码合作时必须遵循的协议。

  • 「服务」 (Service) 初始角色,实现了具体功能的类,但客户端与其接口不兼容, 无法直接调用。

  • 「适配器」 (Adapter) :适配器角色,负责与客户端和服务交互。

    「它在实现客户端接口的同时封装了服务对象。适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用」

  • 「客户端」 (Client):只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。

    向程序中添加新类型的适配器或者服务类的接口被更改或替换时无需修改客户端代码就可以创建新的适配器类。

设计模式(8):适配器模式

四、适配器模式实现方式

  • 「创建客户端接口, 声明客户端与服务交互的行为方法。」

  • 「创建遵循客户端接口的适配器类。」

  • 「在适配器类中添加一个成员变量用于保存对于服务对象的引用。」

    可以通过构造函数对该成员变量进行初始化,或在调用其方法时将该变量传递给适配器。

  • 「实现适配器类客户端接口的所有方法。」

    适配器将实际工作委派给服务对象, 自身只负责接口或数据格式的转换。

  • 「客户端必须通过客户端接口使用适配器。」

    通过客户端接口使用适配器可以在不影响客户端代码的情况下修改或扩展适配器。

注意:使用适配器模式必须确保至少有两个类的接口不兼容:

  1. 一个不能修改的功能性服务类 (第三方或存在众多依赖的类) 。
  2. 被一个或多个客户端类使用服务类。

五、适配器模式的实现

以欧美的三孔插座与国标三孔插座之间的「转换器」为例子。在中国使用欧美的三孔插座,比如苹果6的充电头

  • 「客户端接口」

    /**
    * 客户端接口: 国标的三孔插座
    */

    public interface ChinaPlug {

    public void chinaCharge(String type);
    }

    /**
    * 客户端接口实现类: 被一个或多个客户端类使用服务类
    */

    public class ChinaPlugImpl implements ChinaPlug{
    @Override
    public void chinaCharge(String type) {
    if(!type.equalsIgnoreCase("America")){
    System.out.println("正在使用国标三孔插座充电");
    }else {
    PlugAdapter adapter = new PlugAdapter(new AmericaPlugImpl());
    adapter.chinaCharge(type);
    }
    }
    }
  • 「服务接口」

    /**
    * 服务接口: 欧美的三孔插座
    */

    public interface AmericaPlug {

    public void americaCharge();
    }
    /**
    * 服务接口实现类: 欧美的三孔插座 第三方或存在众多依赖的类
    */

    public class AmericaPlugImpl implements AmericaPlug{

    @Override
    public void americaCharge() {
    System.out.println("正在使用欧美三孔插座充电");
    }
    }
  • 「适配器」

    /**
    * 适配器
    */

    public class PlugAdapter extends ChinaPlugImpl{

    /**
    * 成员变量用于保存对于服务对象的引用
    */

    private AmericaPlug americaPlug;

    public PlugAdapter(AmericaPlug americaPlug) {
    /**
    * 如果AmericaPlug 有多个实现类,可以传类型进来进行初始化,类似工厂模式
    */

    this.americaPlug = americaPlug;
    }

    /**
    * 将实际工作委派给服务对象
    */

    @Override
    public void chinaCharge(String type) {
    americaPlug.americaCharge();
    }
    }
  • 「客户端」

     public static void main(String[] args) throws Exception {
    /**
    * 对于客户端而言,只是在调用ChinaPlugImpl没有变
    */

    ChinaPlugImpl chinaPlug = new ChinaPlugImpl();
    chinaPlug.chinaCharge("America");
    }

六、适配器模式的优缺点

  • 「优点」

    • 可以让任何两个没有关联的类一起运行;
    • 「单一职责原则」:可以将接口或数据转换代码从程序主要业务逻辑中分离。
    • 「开闭原则」:客户端通过客户端接口与适配器进行交互, 就能在「不修改现有客户端代码」的情况下在程序中添加新类型的适配器。
  • 「缺点」

    • 由于C#和JAVA都不支持多重继承,所以最多只能适配一个适配者类。

    • 需要新增一系列接口和类,代码整体复杂度增加。

      有时直接更改服务类使其与其他代码兼容会更简单。

七、适配器模式和其他模式的区别

  • 「适配器模式与桥接模式」
    • 桥接模式:通常会于开发前期进行设计, 使程序的各个部分独立开来以便开发。
    • 适配器模式:通常在已有程序中使用, 让相互不兼容的类能很好地合作。
  • 「适配器模式与装饰模式」
    • 适配器模式:可以对已有对象的接口进行修改。
    • 装饰模式:能在不改变对象接口的前提下「强化对象」功能,并且支持递归组合。
  • 「适配器模式与代理模式」
    • 适配器能为被封装对象提供不同的接口。
    • 代理模式能为对象提供相同的接口。
  • 「适配器模式与外观模式」
    • 外观模式:为现有对象定义了一个新接口,通常会作用于整个对象子系统上。
    • 适配器模式:会运用已有的接口,通常只封装一个对象。

八、总结

在使用对象的适配器模式,多用合成/聚合、少用继承。

在.NET应用中也有适配器模式的具体使用:「用作DataSet和数据源之间的适配器DataAdapter」


原文始发于微信公众号(星河之码):设计模式(8):适配器模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/27176.html

(0)

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!