【一起学系列】之适配器模式:还有外观模式呢

2,008

适配器模式

意图

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

别名:Wrapper包装器

适配器模式的诞生

【产品】:开发小哥,记得我们第一版的需求吗?

【开发】:什么玩意?

【产品】:我们第一版是不是造了很多鸭子啊,现在需要造一点火鸡,但是客户很奇葩,他们要让鸭子拥有火鸡一样的能力,怎么办?

【开发】:把鸭子当火鸡写?

【产品】:不行,火鸡是火鸡,鸭子是鸭子,只有特殊的情况才需要混在一起,怎么办呢?

【开发】:老大,怎么办呢?

【BOSS】:什么火鸡鸭子的,它们不都有自己的接口嘛,俩接口刚好用适配器去做,自己去查查资料!

HeadFirst 核心代码

根据上文,我们至少知道了一点,玩适配器模式得有俩接口?先当个问题吧,咱们待会再说

鸭子接口

/**
 * 鸭子接口
 */
public interface Duck {
    /**
     * 鸭叫
     */
    void quack();

    /**
     * 飞行
     */
    void fly();
}

火鸡接口

/**
 * 火鸡接口
 */
public interface Turkey {
    /**
     * 火鸡叫
     */
    void gobble();

    /**
     * 飞行
     */
    void fly();
}

火鸡实现类

/**
 * 火鸡实现类
 */
public class WildTurkey implements Turkey{
    @Override
    public void gobble() {
        System.out.println("咯咯");
    }

    @Override
    public void fly() {
        System.out.println("我在飞,虽然我飞的很近");
    }
}

关键点啦!适配!

/**
 * 火鸡适配器
 * 实现鸭子接口同时持有火鸡对象,在实现的接口处用火鸡对象的方法填充一下(同时还可以做额外的事情)
 */
public class TurkeyAdapter implements Duck{

    Turkey turkey;

    @Override
    public void quack() {
        turkey.gobble();
    }

    @Override
    public void fly() {
        turkey.fly();
    }

    public TurkeyAdapter(Turkey turkey) {
        this.turkey = turkey;
    }
}

适配器模式的设计思路:

  • Target 定义特定领域接口
  • Client 符合Target接口的对象
  • Adaptee 定义一个存在的接口,需要适配
  • Adapter 适配器

简单来说,当我们需要对两个本不相关的接口混合一起用时,需要用一个适配器实现A接口,持有B对象,再用B对象的方法去填充A接口的方法,同时还可以增加一些其他的逻辑

和装饰器很像?

从编程语言的技巧上来看,的确非常的像,我们来梳理一下其相同点和异同点:

相同点:

  • 实现技巧上,都是实现某个接口,同时持有某个对象,用对象的方法去填充需要实现的方法

不同点:

  • 接口个数不同,装饰器一般是单个接口,而适配器则是2个以上接口混合工作
  • 目的不同,装饰器是为了增强对象的方法,而适配器则是让本不能一起工作的接口混在一起工作

来一波接口适配器

接口适配器在Java中很常见,比如:MouseListener之余MouseAdapter

举个例子:

/**
 * 定义超多的方法
 */
public interface InterfaceClass {
    void a();
    void b();
    void c();
    void d();
    void e();
    void f();
}

用一个抽象类去实现接口:

public abstract class InterfaceAdapter implements InterfaceClass{

    @Override
    public void a() {
        System.out.println("i have override method a");
    }

    @Override
    public void b() {}

    @Override
    public void c() {}

    @Override
    public void d() {}

    @Override
    public void e() {}

    @Override
    public void f() {}
}

这样有什么好处呢?比如MouseAdapter,我们在学习GUI编程的时候肯定用到过,此时我只想重写点击方法,但是直接用接口的话,代码太多了,需要重写一堆的东西,用接口适配器的话,则可以很轻松的减少大量的无用代码,专注自己想要实现的方法即可

什么场景适用

  • 你想使用一个已经存在的类,而它的接口不符合你的需求
  • 你想创建一个可以复用的类,该类可以与其他不相关的类或可能不兼容的类一起工作

Code/生活中的实际应用

现在手机都追求全面屏,安卓手机和耳机之间需要一个转接头工作,此时的转接头就是一个适配器,它持有耳机对象,实现手机插孔的接口,最终提供功能的依然是耳机,但它让本来无法联接的物体产生了联系

UML图

外观模式

意图

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

核心思想:为子系统们提供一套通用的对外接口(高级API)

核心代码

该模式过于简单所以直接上代码~

/**
 * 定义一个顶层接口
 */
public interface Computer {

    void open();
}

public class Cpu implements Computer{
    @Override
    public void open() {
        System.out.println("Cpu Open.");
    }
}

public class Ram implements Computer {
    @Override
    public void open() {
        System.out.println("Ram Open.");
    }
}

public class Ssd implements Computer {
    @Override
    public void open() {
        System.out.println("SSD Open.");
    }
}

外观类

public class FacadeComputer {

    private Cpu cpu;
    private Ram ram;
    private Ssd ssd;

    public FacadeComputer() {
        this.cpu = new Cpu();
        this.ram = new Ram();
        this.ssd = new Ssd();
    }

    /** Cpu On **/
    public void onCpu() {
        this.cpu.open();
    }

    /** Ram On **/
    public void onRam() {
        this.ram.open();
    }

    /** Ssd On **/
    public void onSsd() {
        this.ssd.open();
    }

    /** All On **/
    public void allOn() {
        this.cpu.open();
        this.ram.open();
        this.ssd.open();
    }
}

调用对比

/****
 * 推荐阅读顺序:
 * @see Computer
 * @see Cpu | Ram | Ssd
 * @see FacadeComputer
 */
public static void main(String[] args) {
    // 不使用外观模式
    Computer cpu = new Cpu();
    Computer ram = new Ram();
    Computer ssd = new Ssd();
    cpu.open();
    ram.open();
    ssd.open();

    CodeUtils.spilt();

    // 使用外观模式
    FacadeComputer facadeComputer = new FacadeComputer();
    facadeComputer.allOn();
}

什么场景适用

  • 需要为一个复杂的子系统提供一个简单接口时
  • 客户程序与抽象类的实现部分之间存在着很大依赖性(分离逻辑,提高子系统的独立性和可移植性)
  • 需要构建一个层次结构的子系统时

Code/生活中的实际应用

外观模式在生活中最好的体现就是基金,基金的本质其实是由专业的团队去集资购买股票,购买股票本身有非常多的流程和注意事项,但是现在我们可以很轻松的下注基金,这就是外观模式的体现之一

相关代码链接

GitHub地址

  • 兼顾了《HeadFirst》以及《GOF》两本经典书籍中的案例
  • 提供了友好的阅读指导