【设计模式】适配器模式

205 阅读7分钟

本文主要介绍:类适配器模式,双向适配器模式,缺省适配器模式。

模式背景

在我们生活中就存在着很多适配器模式,比如电源适配器,最新的苹果耳机接口(共用充电口的那个),插座适配器。这些都是因为我们现有的一些物件和外界提供的物件无法正确匹配,当需要能使用对方的时候,就需要一个中间的适配器来适配他们。

在开发中性质其实也是一样,我们现有的工程项目一直以来都依赖这一个老的模块(就像是我们2个孔的插座),现在因为某种原因我们要替换该模块使用一个别的模块(比如市面上有新的模块了,然后这个模块比老模块实现更好更强大),但是这个新模块的接口和我们原来使用的不一样(就像是我们一直用的2孔插座,现在眼前只有3孔插座)。这时候我们就需要一个适配器来让我当前的项目,适配到这个新模块上(我们要一个适配器,2孔的插上去就能再插3孔的了)。

定义&概念

将一个接口转换为客户端希望的另一个接口,使得那些接口不兼容的类可以一起工作,其别名为包装器(Wrapper)。适配器模式是一种类结构性模式,也可以是对象结构型模式

适配器模式可以分为对象适配器模式类适配器模式。对象适配器中,适配器和适配者之间是关联关系。类适配器中,适配器和适配者之间是继承(或实现)关系。实际开发中对象适配器的使用频率更高。

原理

适配器模式通过增加一个新的适配器类来解决接口不兼容的问题,使得原来没有任何关系的类可以协同工作。

基本要素

  • 适配者
    • 就是我们实际想要使用模块,他可以是一个类或接口,也可以是一个对象,只是这个适配者我们不能直接使用。
  • 适配器
    • 具体的转换器,通过继承适配者,或者组合适配者,来调用适配者的方法。
  • 目标抽象类
    • 用来定义客户需要使用的接口,他可以是一个抽象类或者接口。
    • 他约束了适配器类的行为,同时也是为了方便系统对适配器类进行扩展(添加新的Adapter实现)。

UML

实现

对象适配器

适配器内部持有一个被适配的对象,然后通过这个对象类转发调用的。

具体需要被适配的类

public interface IAdaptee {
    void adapteeFunction();
}
public class Adaptee implements IAdaptee {
    @Override
    public void adapteeFunction() {
        // 客户端使用方 想要调用的方法
        System.out.println("adaptee function");
    }
}

客户端使用的接口

public interface ITarget {
    void userFunction();
}

适配器类(注意核心:内部组合的adaptee对象)

class ObjectAdapter implements ITarget {
    /**内组合一个被适配的对象*/
    Adaptee adaptee;
    @Override
    public void userFunction() {
        adaptee.adapteeFunction();
    }
}

类适配器

只是将对象适配器中组合关联的 被适配对象使用继承的方式来实现适配器类。

(注意核心:extends Adaptee类 )

class ClassAdapter extends Adaptee implements ITarget {
    @Override
    public void userFunction() {
        this.adapteeFunction();
    }
}

装饰模式区别

适配器模式的思想其实和装饰者模式很像,并且他们都属于结构型模式,很容易会混淆他们用法。他们实现的手法思想虽然相似,但是各自的目的却不一样。

适配器模式的目的:现有的这个接口挺好的,只是我们这边客户端无法兼容它,他强调在于适配,只是适配一下然后原样调用适配者方法,我们不会去修改原先的接口(当然你非要修改也拦不住你,但是修改了就不属于适配的思想了,适配器主要的工作应该是将原来无法兼容的接口,通过你的操作变成兼容的,本质上不修改原来接口的任何逻辑)。

装饰者模式的目的:现有的这个接口功能不够,我们希望对这个接口新加一些功能,对接口做扩展,他强调在于扩展。他会修改原先的接口实现。

优缺点

优点

  • 将目标类和适配者进行解耦,当需要新的适配的时候,只需要添加新的适配器,不需要修改代码。
  • 让同一个适配者类,可以在多个不同的系统中复用。提高了适配者的复用性。(因为市面上有好多转换器,所以我买手机买Type-C接口的都能找到对应的适配器来充电)

缺点

  • Java,C#这些单继承的语言,一次只能继承一个适配者类,不能同时适配多个适配者。
  • 适配者类不能是final的。
  • 即对使用的编程语言存在局限性。

使用场景

  • 系统需要使用现有的类,但现有的类却不兼容。
  • 需要建立一个可以重复使用的类,用于一些彼此关系不大的类,并易于扩展,以便于面对将来会出现的类。
  • 需要一个统一的输出接口,但是输入类型却不可预知。

实例

  • 在系统重构的时候,发现以前人写的接口规范已经返回的数据有有问题,所以我重构了这些接口,但是老接口系统已经在用了,不能让前端再针对你的去全改下,这时候我就对我新写的接口加了一个适配层。这样老接口保留着,前端后面直接使用新的接口。

扩展

双向适配器

**定义:**在一个适配器中既包含用户端的方法,又包含被适配端的方法。使得通过该适配器可以当做A到B的适配,也能当做B到A的适配。

即做了一个适配器:这个适配器是客户端对适配者的适配,也是适配者对客户端的适配,所以这个适配器中需要有客户端的引用,和适配者的引用。

实现

class TwoWayAdapter implements IAdaptee, ITarget {

    private IAdaptee adaptee;
    private ITarget target;
		/**我们想给客户端用 就可以使用被适配的对象初始化它*/
    public TwoWayAdapter(IAdaptee adaptee) {
        this.adaptee = adaptee;
    }
		/**我们想让被适配的类使用客户端的,则反之*/
    public TwoWayAdapter(ITarget target) {
        this.target = target;
    }

    /**
     * 给客户端用
     * 可以作为为客户端 适配的适配器
     */
    @Override
    public void adapteeFunction() {
        target.userFunction();
    }

    /**
     * 给被适配类用,被适配的类成了客户端
     * 也可以作为为被适配器类 适配了客户端
     */
    @Override
    public void userFunction() {
        adaptee.adapteeFunction();
    }
}

缺省适配器

这个也是使用很广泛的一个适配器模式:对于一个接口,我们不想实现这个接口所有的方法,可以使用一个抽象类来实现该接口并提供一个默认实现。然后再让子类去覆盖自己想要实现的方法。

这么做的好处:

当一个接口有很多方法的时候,我们项目中需要如果很多类需要实现这个接口的时候,那么就要每个类都要实现这个接口的所有方法,这么很费劲。可以在中间加入一个抽象类,这样具体的类只要覆盖这个抽象类就行了。

必要条件

  • 一个接口,最好是有很多方法
  • 一个抽象类,实现该接口提供默认实现
  • 具体的业务类,继承抽象类覆盖所需要的逻辑

实现

/**
 * 一个很多方法的接口
 */
interface ServiceInterface {
    void f1();

    void f2();

    void f3();

    void f4();
}

/**
 * 缺省适配器
 * 使用抽象类适配一版本,提供接口默认实现
 */
abstract class AbastractServiceClass implements ServiceInterface {
    @Override
    public void f1() {
        System.out.println("abs f1");
    }

    @Override
    public void f2() {
        System.out.println("abs f2");
    }

    @Override
    public void f3() {
        System.out.println("abs f3");
    }

    @Override
    public void f4() {
        System.out.println("abs f4");
    }
}

/**
 * 对应具体业务去实现对应的接口方法即可。
 */
class Service1 extends AbastractServiceClass {
    @Override
    public void f1() {
        System.out.println("service 1");
    }
}

class ServiceN extends AbastractServiceClass {
    @Override
    public void f4() {
        System.out.println("service 4");
    }
}

相关代码:github.com/zhaohaoren/…

如有代码和文章问题,还请指正!感谢!