适配器模式概述

我们都见过电脑的电源适配器,它的作用就是将 220V 的家用交流电通过变压器变压,再通过整流器变直流,这样就得到了电脑可用的 12V 直流电。在这个过程中适配器就是将不可用、不合适的 220V 交流电转变为可用的 12V 直流电供电脑使用。

在软件开发过程中也会出现类似的情况。

假如你在开发一款股价走向预测软件,它会从不同来源下载 XML 格式的股票数据,然后向用户呈现美观的图表。

在开发过程中,你决定在程序中整合一个第三方的智能分析程序库,但是有个问题,这个程序库只兼容 JSON 格式的数据。

你无法 “直接” 使用分析函数库,因为它所需的输入数据格式与你的程序不兼容。

你可能无法修改分析函数库的代码,因为它可能正被其他代码引用,此外更糟糕的是你可能也没有它的源码。

此时,只兼容 JSON 的分析函数库就相当于 220V 的交流电,而你的应用程序就像只支持 10V 直流电的电脑,此时你需要一个电源适配器,通过适配器将只兼容 JSON 的分析函数库转变为可支持 XML 的函数库。

适配器模式解析

通过刚才的描述,相信大家都对适配器模式的作用有了一定的概念。适配器能将一个类的接口转变为另一个客户端想要的接口,使得原本不能一起工作的接口能一起工作。

根据适配器类和适配者类的关系,适配器模式分为类适配器和对象适配器。

适配器模式包含三个角色:适配者、适配器、目标接口。

  • 适配者:已存在的接口,包含了客户端想要使用的业务接口,常见于第三方 jar 包,没有源码,类比于 220V 交流电;
  • 目标接口:是客户所需要的接口,类比于电脑;
  • 适配器:作为一个转换器,通过继承或引用适配者,把适配者接口转换成目标接口,供客户端访问,类比于电源适配器。

适配器模式的使用场景

那么哪些情况下适用适配器模式呢?

当你希望使用某个类,但是其接口与其他代码不兼容时,可以使用适配器类。

举个栗子

可以参考上面股票分析软件和第三方智能分析程序库,他俩本身不能一起工作,但是通过一个适配器,就可以做到了。

当你想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些类不一定有一致的接口。

举个栗子

意思就是通过一个类 A,他来提供一个兼容性比较高的外观,通过适配器的作用,来使其他类 B、C、D 能一起对外提供服务。

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

举个栗子

比如有一个鸭子类,还有一个火鸡类,现在我们要把火鸡加入到鸭子中,这样我们就得到了一只看起来像鸭子的火鸡。

对象适配器

对象适配器的实现方式为适配器实现目标接口,并且依赖于适配者。

下面为对象适配器的类图。

对象适配器类图

接下来用一个小小的例子来感受一下:

public class RoundHole {

    private double radius;

    public RoundHole(double radius){
        this.radius = radius;
    }

    public void fits(RoundNail roundNail){
        if(radius >= roundNail.getRadius()){
            System.out.println("半径为"+roundNail.getRadius()+"的钉子和半径为"+radius+"洞可以契合");
        }else{
            System.out.println("半径为"+roundNail.getRadius()+"的钉子和半径为"+radius+"洞不能契合");
        }
    }
}
public class RoundNail {

    private double radius;

    public RoundNail(double radius) {
        this.radius = radius;
    }

    public RoundNail(){

    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }
}
public class SquareNail {

    private double width;

    public SquareNail(double width) {
        this.width = width;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }
}
public class NailAdapter extends RoundNail {

    private SquareNail squareNail;

    public NailAdapter(SquareNail squareNail) {
        this.squareNail = squareNail;
    }


    @Override
    public double getRadius(){
        double width = squareNail.getWidth();
        double radius = width/2 * Math.sqrt(2);
        return radius;
    }
}
public class Client {

    public static void main(String[] args) {
        SquareNail squareNail1 = new SquareNail(4);
        SquareNail squareNail2 = new SquareNail(8);

        NailAdapter adapter1 = new NailAdapter(squareNail1);
        NailAdapter adapter2 = new NailAdapter(squareNail2);

        RoundHole roundHole = new RoundHole(4);
        roundHole.fits(adapter1);
        roundHole.fits(adapter2);
    }
}

输出:

半径为 2.8284271247461903 的钉子和半径为 4.0 洞可以契合
半径为 5.656854249492381 的钉子和半径为 4.0 洞不能契合

这个例子就是 SquareNail 类和 RoundHole 类不能兼容,因为 RoundHole 只支持 RoundNail,RoundHole 与 SquareNail 不能一起工作,所以使用了适配器将 SquareNail 转变为了 RoundNail,这样使得 RoundHole 与 SquareNail 能一起工作。

对象适配器的优点:

  • 一个对象适配器可以把多个不同的适配者适配到同一目标接口
  • 可以适配一个适配者的子类,根据“里氏替换原则”,适配者的子类也可以通过该适配器进行适配

类适配器

类适配器的实现方式为适配器继承适配者,并且实现目标接口。

下面为类适配器的类图:

类适配器类图

类适配器模式的优点:

  • 可以在适配器类中重写一些适配者的方法,使得适配器的灵活性更强

类适配器模式的缺点:

  • 对于不支持多重继承的语言,一次最多只能适配一个适配者类;
  • 适配者类不能为最终类;
  • 在 Java、C# 等语言中,类适配器模式中的目标抽象类只能为接口,不能为类。

缺省适配器

缺省适配器是适配器模式的一种变体,应用也较为广泛。

实现方式为先设计一个声明大量方法的接口 A,然后设计一个抽象类 B 实现接口 A,并使用空方法的形式实现接口 A 中的方法,是缺省适配器模式的核心类,最后再设计一个具体业务类 C,他继承于抽象类 B,可以有选择性的覆盖 B 中的方法。

下面为缺省适配器模式的类图:

在这里插入图片描述

将枚举适配到迭代器

我们经常会发现有些遗留的代码会返回一个枚举器接口 Enumeration,但是我们想新的代码中只使用迭代器接口 Iterator,现在动手试试构造一个适配器解决这个问题。

目标接口 Iterator 如下:

在这里插入图片描述

适配者接口 Enumeration 如下:

在这里插入图片描述

编写一个 EnumeratorIterator 适配器:

public class EnumeratorIterator implements Iterator {

    private Enumeration enumeration;

    public boolean hasNext() {
        return enumeration.hasMoreElements();
    }

    public Object next() {
        return enumeration.nextElement();
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

适配器模式与其他模式的关系和区别

桥接模式通常会于开发前期进行设计,使你能够将程序的各个部分独立开来以便开发。而适配器模式通常在已有程序中使用,让相互不兼容的类能很好地合作。

适配器可以对已有对象的接口进行修改,装饰模式则能在不改变对象接口的前提下强化对象功能。此外,装饰还支持递归组合,适配器则无法实现。

适配器能为被封装对象提供不同的接口,代理模式能为对象提供相同的接口,装饰则能为对象提供加强的接口。

外观模式为现有对象定义了一个新接口,适配器则会试图运用已有的接口。适配器通常只封装一个对象,外观通常会作用于整个对象子系统上。

桥接、状态模式和策略模式(在某种程度上包括适配器) 模式的接口非常相似。实际上,它们都基于组合模式——即将工作委派给其他对象,不过也各自解决了不同的问题。模式并不只是以特定方式组织代码的配方,你还可以使用它们来和其他开发者讨论模式所解决的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值