前言

今天我们来看另一个和包装有关的设计模式——适配器模式,和装饰者模式比起来,它更像是一位无名耕耘者,隐身于沟渠之中,干着脏活累活——转换接口。用时下比较火的一个词来比喻这种设计模式的话,那就是女装大佬,这真的是适配器模式最佳类比了,我相信你看完今天的所有内容,一定会明白的

适配器模式

适配器模式将一个类的接口,转换成客户期望的另一个接口,适配器让原本不兼容的接口可以合作无间。

适配器模式采用的设计原则是使用对象组件,以修改的接口包装被适配者,这样的好处是,被适配者的任何子类,都可以搭配适配器使用。

设计模式详解——适配器模式_包装类

采用适配器模式之后:

设计模式详解——适配器模式_包装类_02

要点

  • 适配器模式主要是为了在不改变原有接口的基础上,适配客户新的接口
  • 当需要使用一个现有的类而其接口并不符合你的需要时,就使用适配器模式
  • 适配器有两种形式:对象适配器和类适配器。类适配器需要用到多重继承
  • 适配器将一个对象包装起来以改变起接口;装饰者将一个对象包装起来以增加新的行为和属性;而外观模式将一群对象“包装”起来以简化其接口

示例代码

从网上搜了一些适配器的示例,但是最后发现还是没有《Head First设计模式》中的简单,所以最后我还是用了书上的示例。

示例的场景是这样的:假设客户需要一只鸭子,但是我们手里只有火鸡,这时候为了满足客户的需求,我们必须通过适配器将火鸡转换成鸭子。

下面就是整个实现过程:

原始系统接口

首选我们拿到鸭子的接口,其中有两个方法,要给是鸣叫,一个是飞

public interface Duck {
    void quack();
    void fly();
}
接口实现类

这个是鸭子的实现,这是仅仅是为了后期测试。因为我们现在没有这个实现,所以后面要适配这样一个对象。

public class MallardDuck implements Duck {
    @Override
    public void quack() {
        System.out.println("quack");
    }

    @Override
    public void fly() {
        System.out.println("I'm flying");
    }
}
被适配对象接口

这个是我们火鸡的原始接口,也就是我们要适配成鸭子的火鸡的基类,也有两个方法,一个是鸣叫,一个是飞。

public interface Turkey {
    void gobble();
    void fly();
}

火鸡的实现类:

public class WildTurkey implements Turkey {
    @Override
    public void gobble() {
        System.out.println("gobble gobble");
    }

    @Override
    public void fly() {
        System.out.println("I'm flying a short distance");
    }
}
适配器实现

这里就是火鸡适配鸭子的适配器,首先它要实现鸭子的接口,然后我们定义一个火鸡属性,用于接收我们的火鸡实例,再然后我们将火鸡的鸣叫和飞的方法分别封装到鸭子的对应方法,因为要是一直飞,火鸡是普鲁普鲁飞,所以要多次飞。至此 ,火鸡适配鸭子的适配操作就完成了,下面我们测试下。

public class TurkeyAdapter implements Duck {
    Turkey turkey;

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

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

    @Override
    public void fly() {
        for (int i = 0; i < 5; i++) {
            turkey.fly();
        }
    }
}
测试代码

这里我们分别创建了鸭子、火鸡和适配了鸭子的火鸡的示例,演示他们的执行过程:

 @Test
    public void testDuck() {
        MallardDuck duck = new MallardDuck();

        WildTurkey turkey = new WildTurkey();
        TurkeyAdapter turkeyAdapter = new TurkeyAdapter(turkey);
        System.out.println("火鸡:");
        turkey.gobble();
        turkey.fly();

        System.out.println("=============\n绿头鸭:");
        duck.quack();
        duck.fly();

        System.out.println("=============\n火鸡适配的鸭子:");
        turkeyAdapter.quack();
        turkeyAdapter.fly();
    }

从代码中我们可以看到,这里适配了鸭子的火鸡像鸭子一样执行了quackfly操作

运行结果:

设计模式详解——适配器模式_包装类_03

总结

从上面的示例中,我们可以很清楚的看到,适配器模式本质上就是类型的转换,除了我们这里的例子,日常生活中还有很多类似的例子,比如电脑显示器VGAHDMI、用电设备插头转换等:

设计模式详解——适配器模式_装饰者模式_04

设计模式详解——适配器模式_ide_05

虽然都是类型的包装和转换,但是适配器模式和装饰者模式是有本质区别的,装饰者模式始终是同同一种类型的包装和转换,也就是说所有的包装类于被包装类,本质上都是同一种类型,但是在适配器中,转换和包装的一定是不同的类型,比如火鸡到鸭子。

最后引用一段话来结束今天的内容:

如果它走起来像只鸭子,叫起来像只鸭子,那么它必定可能是一只鸭子包装了鸭子适配器的火鸡……