译-设计模式-结构模式之Adapter

来源:互联网 发布:冒险岛伴侣官方域名 编辑:程序博客网 时间:2024/05/16 12:16

翻译整理自:https://refactoring.guru/design-patterns/adapter

更多请移步: 我的博客

目的

适配器是一种结构设计模式,使得不兼容接口间的可正常进行协作。

问题

想象你有一个使用XML作为数据处理格式的APP,但你用到了一个仅支持JSON数据格式的类库。

举个例子,你有一个做股票数据的APP。他从多个数据源获取XML来展示成图标。在一个新版本中,你决定使用一些三方的分析包。但是有一个问题:分析包仅支持JSON数据。

xmlcant2json.png

在这种情况下,可以重写你自己的代码以便支持JSON或者改变依赖的库使其支持XML。第一种选择要破坏已有的代码,第二种选择看上去是不可能的,因为我们常常无法修改三方库。

解决方法

你可以创建一个适配器。它可以将调用方发送的数据按照格式转换成三方库可以解析的类型。适配器封装了对一个对象复杂的转换过程。

适配器可以不仅可以格式化数据,也可以适配接口。比如,适配器接收到一个对方法A的调用,他可以转交给被包装的方法B、C、D。

有时甚至可以创建一个双向适配,这样就可以双向转换。

xml2json

综上讨论,股票超市APP需要一个特别的XML_To_JSON_Adapter类。在调用分析类库之前,将xml转换为json。采用这种方式你将不需要改变任何已有的APP代码,也不需要改变分析库的代码。

真实世界的类比

不同国家的插头和插板

当你第一次从美国到偶主,你会发现没办法给你的笔记本充电。两个国家插座和插头的标准根本不一样。这就是为什么美国的插头没办法适配德国的插座。

这个问题可以通过使用具有美式插座和欧式插头的电源插头适配器来解决。

结构

对象适配

实现采用组合原则:适配器实现其中一对象的接口,并且封装另一个对象。他可以在所有新生代语言中实现。

objectAdapter

类适配

实现采用集成。适配器同是继承两个接口。该方式只有像C++这种支持多继承的语言可以实现。

classAdapter

  1. Existing interface (存在的接口或者类)已经被你的其余代码支持

  2. Service (服务)是一些不能在应用类直接工作的有用类(通常是三方库或者遗留代码)

  3. Adapter 实现了 Existing interface 并且持有 Service 类引用。

    adapter会接收到client通过 Existing interface 定义的方法调用。他在调用 Service 前可能会修正调用参数的类型或者格式化数据。

  4. Client 使用 Adapter 调用 Existing interface 中定义的接口。

    这里允许添加新的 Adapter 来编程而不需要改动已经存在的代码(Service 改变的的情况可能也会出现,比如,当你更新依赖的三方库)。

  5. 这个adapter类不需要封装任何对象。他同时实现了两个接口。因此,对所有的对象都是兼容的。

伪代码

让我们看看Adapter如何从一个接口到另一个接口进行基本数据转换。这个例子给予圆孔和方形钉的冲突。圆孔可以和圆钉很好的工作;可以通过两者的半径来决定是否合适。但是方形钉不能测量半径。

pseudocode

这就是我们为什么需要创建一个Adapter类来封装方形钉对象并且伪装他有一个等于方形直径一半的半径。

// Classes with compatible interfaces: RoundHole and RoundPeg.class RoundHole is    constructor RoundHole(radius) { ... }    method getRadius    method fits(peg: RoundPeg) is        return this.getRadius() >= peg.radius()class RoundPeg is    constructor RoundPeg(radius) { ... }    method getRadius() is        Return the peg radius.// Obsolete incompatible class: SquarePeg.class SquarePeg is    constructor SquarePeg(width) { ... }    method getWidth() is        Return the square peg width.// Adapter allows fitting square pegs into round holes.class SquarePegAdapter extends RoundPeg is    private field peg: SquarePeg    constructor SquarePegAdapter(peg: SquarePeg) is        this.peg = peg    method getRadius() is        return Math.sqrt(Math.pow((peg.getWidth()/2), 2) * 2);// Somewhere in client code.hole = new RoundHole(5)rpeg = new RoundPeg(5)hole.fits(rpeg) // truesmall_sqpeg = new SquarePeg(2)large_sqpeg = new SquarePeg(5)hole.fits(small_sqpeg) // won't compile (incompatible types)small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg)large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg)hole.fits(small_sqpeg_adapter) // truehole.fits(large_sqpeg_adapter) // false

适用性

当你想服用已经存在的类,但是接口和应用其他代码不兼容

采用适配器模式创建一个中间层来将调用转换成应用中已存在对象可以处理的数据。

你需要复用几个已经存在的类,但是她们缺少一些常用的功能。并且你无法在父类中添加这些功能,因为他是闭源或者被其他代码使用的。

你可以把这些缺少的功能放到新建的adapter中。他将连接你应用的代码和你感兴趣的类。这种解决方式看起来很像Visitor模式。

如何实现

  1. 确保你有两种元素:

    • 有用的service对象
    • 应用代码必须使用service对象。应用不能够直接调用service,因为接口或者数据格式不兼容。
  2. 声明后面需要adpater跟随的client接口。应用将使用这个接口和adapter交互。

  3. 创建一个adpater,实现client接口(空实现)。

  4. adapter增加一个service变量。通常情况下,这个变量在构造方法中设置。简单的场景下,适配器可直接转发调用(直接调用service中对应的方法)。

  5. 实现client定义的接口。adapter方法直接调用service中适当的方法并传递格式化后的数据。

  6. adapter类编写完成后,在应用中通过client接口来使用它。

优缺点

  • 优点:隐藏了客户端代码不需要知道的接口实现细节和数据转换

  • 缺点:引入新的类,使得整体复杂度增加

和其他模式的联系

  • Bridge(桥接)是先期设计把抽象和实现独立。Adapter是进行改装,使没有关系的类在一起工作。Adapter是在设计后期使一些功能工作;Bridge在前期做这些事情。

  • Adapter为了实现目标,提供不同的接口。Proxy(代理)提供相同的接口。Decorator(装饰)提供增强的接口。

  • Adapter意味着改变现有代码的接口。Decorator另一个对象的接口但是没有改变接口。因而Decorator比Adapter更透明。结果就是,Decorator支持递归组合,纯粹的适配器做不到这点。

  • Facade(门面)定义了一个新接口,而Adapter复用已有接口。记住,Adapter使两个存在的接口协作而不是完全定义一个新接口。

  • Sate(状态),Strategy(策略),Bridge(某种程度上的Adapter)有类似的解决结构。他们都是共享”handle/body”元素。他们的意图不同,所以,他们解决不同的问题。

Java中模式的使用

用例:Adapter模式在Java代码中很常见。它经常用于基于一些遗留代码的系统中。在这种情况下,Adapter使得老代码变得符合现在的需要。

在Java核心库中有一些标准的Adapter:

  • java.util.Arrays#asList()

  • java.util.Collections#list()

  • java.util.Collections#enumeration()

  • java.io.InputStreamReader(InputStream) (returns a Reader object)

  • java.io.OutputStreamWriter(OutputStream) (returns a Writer object)

  • javax.xml.bind.annotation.adapters.XmlAdapter#marshal() and #unmarshal()

原创粉丝点击