手记

Head First设计模式读书总结——适配器模式与外观模式

还记得装饰者模式吗?我们将对象包装起来,赋予它们的职责。而现在则是以不同目的,包装某些对象:让它们的接口看起来不像自己而像是别的东西。为何要这样做?因为这样就可以在设计中,将类的接口转换成想要的接口,以便实现不同的接口。不仅如此,我们还要探讨另一个模式,将对象包装起来以简化其接口。
题例:回到第一章的鸭子中来。
如果它走起路来像只鸭子,叫起来像只鸭子,那他可能是一只包装了要适配器的火鸡。让我们看看鸭子接口和一个稍微简化的版本:

interface Duck2{
    public void quack();
    public void fly();
}

绿头鸭是鸭子的子类:

class MallardDuck2 implements Duck2{

    @Override
    public void quack() {
        System.out.println("Quack");
    }

    @Override
    public void fly() {
        System.out.println("I'm flying");
    }
}

为您介绍最新的“街头顽禽”火鸡:

interface Turkey{
    public void gobble();
    public void fly();
}
class WildTurkey implements Turkey{

    @Override
    public void gobble() {
        System.out.println("Gobble gobble");
    }

    @Override
    public void fly() {
        System.out.println("I'm fly a short distance");
    }
}

现在,假设你缺鸭子对象,想用一些火鸡对象来冒充。显而易见,因为火鸡的接口不同,所以我们不能公然拿来用。那么,就用到适配器了:

class TurkeyAdapter implements Duck2{
    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();
        }
    }
}

客户使用适配器的过程如下:
1:客户通过目标接口调用适配器的方法对适配器发出请求。
2:适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口。
3:客户接受到调用的结构,但并未察觉这一切是适配器在起转换作用。
定义适配器模式
适配器模式将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。

这个模式可以通过创建适配器进行接口转换,让不兼容的接口变成兼容。这可以让客户从实现的接口解耦。如果在一端时间之后,我们想要改变接口,适配器可以将改变的部分封装起来,客户就不必为了应对不同的接口而每次跟着修改。
对象和类的适配器
实际上有“两种”适配器:“对象”适配器和“类”适配器。上面是对象适配器。
那什么是类适配器。你需要多重继承才能够实现它,这在java中是不可能的,但是当你在使用多重继承语言的时候,还是可能遇到这样的需求,让我们看看类图:

唯一的差别就在于适配器继承了Target和Adaptee。而对象适配器利用组合的方式将请求传送给被适配者。
真实世界的适配器
如果你已经使用过java,可能记得早期集合(collection)类型(例如:Vector、Stack、Hashtable)都实现了一个名为elements()的方法。该方法会返回一个Enumeration(举)。这个Enumeration接口可以逐一走过此集合内的每个元素。而无需知道它们在集合内是如何被管理的。

public interface Enumeration<E> {
    boolean hasMoreElements();
    E nextElement();
}

新世界的迭代器
当Sun推出更新后的集合类时,开始使用了Iterator(迭代器)接口,这个接口和枚举接口很像,都可以让你遍历此集合类型内的每个元素,但不同的是,迭代器还提供了删除元素的能力。

public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

而现在
我们经常面对遗留代码,这些代码暴露出枚举接口,但我们又希望在新的代码中只使用迭代器。就需要构造一个适配器了。
设计适配器
我们需要一个适配器,实现了目标接口,而此目标接口是被适配者所组合的,类图是这样的:

处理remove()方法
我们知道枚举不支持删除,适配器无法实现一个又实际功能的remove()方法,最多只能抛出一个运行时异常。幸运地,迭代器接口的设计者事先料到了这样的需要,所以将remove()方法定义成会抛出UnsupportedOpeartionException。
编写一个Enumeratorlterator适配器

class Enumeratorlterator implements Iterator{
    Enumeration enumeration;
    public Enumeratorlterator(Enumeration enumeration){
        this.enumeration=enumeration;
    }
    @Override
    public boolean hasNext() {
        return enumeration.hasMoreElements();
    }

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

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

本章中,还有另一个模式。
你已经知道适配器模式是如何讲一个类的接口转换成另一个符合客户期望的接口的。你也知道在Java中要做到这一点,必须将一个不兼容接口的对象包装起来,变成兼容的对象。
我们现在要看一个改变接口的新模式,但是它改变接口的原因是为了简化接口,这个模式被巧妙的命名为外观模式。它将一个或数个类的复杂的一切都隐藏在背后,只显露出一个干净美好的外观。
题例:家庭影院
通过一番研究,你组装了一套杀手级的系统,内含DVD播放器、投影仪,主动屏幕、环绕立体声、甚至还有爆米花机。看看这些组件:

想看电影,必须先执行一些任务:
1:打开爆米花机
2:开始爆米花
3:将灯光调暗
4:放下屏幕
等等之类十几个步骤
你需要的正是一个外观:有了外观模式,通过实现一个更合理的接口的外观类,你可以将一个复杂的子系统变的容易使用。看看改变之后的类图:

构造家庭姻缘外观:


定义外观模式
外观模式提供了一个统一的接口,用来访问自系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
我们就创建了一个接口简化而统一的类,用来包装子系统中一个或多个复杂的类,外观模式相当直接、很容易理解。外观模式允许我们让客户与子系统之间避免紧耦合。

“最少知识”原则
设计原则:最少知识原则:只和你的密友谈话。
告诉我们要减少对象之间的交互,只留下几个“密友”。

1人推荐
随时随地看视频
慕课网APP