适配器模式

369 阅读3分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

适配器模式

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

优点

  • 客户端通过适配器可以透明地调用目标接口。
  • 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
  • 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
  • 在很多业务场景中符合开闭原则。

缺点

  • 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
  • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱

结构

  1. 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  2. 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
  3. 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

image.png

  • 冲突:Target期待调用Request方法,而Adaptee并没有(这就是所谓的不兼容了)。

  • 解决方案:为使Target能够使用Adaptee类里的SpecificRequest方法,故提供一个中间环节Adapter类 (继承Adaptee & 实现Target接口) ,把Adaptee的API与Target的API衔接起来(适配)。

演示

类适配器

1、定义目标接口

public interface TypeCChargerTarget {
    void useTypeCCharger();
}

2、定义目标接口实现类

public class TypeCChargerTargetImpl implements TypeCChargerTarget{
    @Override
    public void useTypeCCharger() {
        System.out.println("使用type-c充电线充电!");
    }
}

3、定义适配者接口

public interface TraditionChargerAdaptee {
    void useTraditionCharger();
}

4、定义适配者接口实现类

public class TraditionChargerAdapteeImpl implements TraditionChargerAdaptee {
    @Override
    public void useTraditionCharger() {
        System.out.println("使用传统充电线充电!");
    }
}

5、定义适配器

public class Adapter extends TraditionChargerAdapteeImpl implements TypeCChargerTarget {
    @Override
    public void useTypeCCharger() {
        System.out.println("转换充电线");
        super.useTraditionCharger();
    }
}

6、客户端

public class Client {
    public static void main(String[] args) {
        TypeCChargerTargetImpl typeCChargerTargetImpl = new TypeCChargerTargetImpl();
        typeCChargerTargetImpl.useTypeCCharger();
        Adapter adapter = new Adapter();
        adapter.useTypeCCharger();
        // 使用type-c充电线充电!
        // 转换充电线
        // 使用传统充电线充电!
    }
}

这个就是类适配器:通过定义适配器来实现当前业务接口,同时继承现有接口进行转换;

对象适配器

相对的就还有对象适配器;先看代码不同点,再进行两者比较

1、修改适配器

public class Adapter  implements TypeCChargerTarget {
    private TraditionChargerAdapteeImpl traditionChargerAdapteeImpl;
    public Adapter(TraditionChargerAdapteeImpl traditionChargerAdapteeImpl) {
        this.traditionChargerAdapteeImpl = traditionChargerAdapteeImpl;
    }
    @Override
    public void useTypeCCharger() {
        System.out.println("转换充电线");
        traditionChargerAdapteeImpl.useTraditionCharger();
    }
}

2、修改客户端

public class Client {
    public static void main(String[] args) {
        TypeCChargerTargetImpl typeCChargerTargetImpl = new TypeCChargerTargetImpl();
        typeCChargerTargetImpl.useTypeCCharger();
        Adapter adapter = new Adapter(new TraditionChargerAdapteeImpl());
        adapter.useTypeCCharger();
        // 使用type-c充电线充电!
        // 转换充电线
        // 使用传统充电线充电!
    }
}

改动点有两个地方

  1. 适配器类不需要继承现有的实现接口
  2. 客户端要明确指定需要转换的现有实现接口

所以可以总结出两种存在的不同点

1、类的适配器中Adapter与Adaptee是继承关系,但是java是一个类只能继承一个类,所以每个类的适配器就只能适配一种。对象适配器是将实现接口类定义成成员变量,这样就可以适配多种

2、类的适配器使用继承的方式,造成高耦合,灵活性不足;对象的适配器需要引入对象实例,所以适配前需要明确指定要适配的对象,使用相对复杂。

总结

适配器的目的是在不改变原接口的基础上向客户提供预期的服务,所以不管是哪种适配器,都是可以满足的,当然我个人推荐对象的适配器,比较相对灵活,适配不局限一种实现。