Head First 设计模式总结(七) 适配器模式

本文基于《Head First 设计模式》对其中的适配器模式予以总结

适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
问题描述:假设有一个软件系统,你希望它能和一个新的厂商类库搭配使用,但是新厂商设计出来的接口与旧厂商不同。而你又不想改变现有的代码,该怎么办?

答案:写一个适配器类,将新厂商接口转换成你期望的接口。
在这里插入图片描述

下面先利用适配器完成一个简单的转换

假设Duck接口是我们期望的接口

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

绿头鸭是Duck的子类,它可以实现Duck接口具备具体的能力

public class MallardDuck implements Duck{

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

现在新厂商设计出来的并不是Duck接口,而是Turkey接口,那么怎么像之前使用Duck接口那样去使用这个新的接口呢?
这是新做的Turkey接口

public interface Turkey{
        public void gobble();//火鸡不会quack,只会gobble
        public void fly();//火鸡会飞,但是飞不远
    }

这是一个野鸡WildTurkey去实现了Turkey接口

public class WildTurkey implements Turkey{
        public void gobble(){
            System.out.println("Gobble gobble");
        }
        public void fly(){
            System.out.print("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();
        }
    }
}

下面是测试类

public class DuckTestDrive {
    public static void main(String[] args){
        System.out.println("....................the turkey says...............................");
        WildTurkey wildTurkey = new WildTurkey();
        wildTurkey.gobble();
        wildTurkey.fly();

        
        System.out.println(".....................the turkey adapter says...................");
        Duck turkeyAdapter = new TurkeyAdapter(wildTurkey);
        turkeyAdapter.quack();//wildTurkey装进了适配器中,因此它在外界看来就是一只鸭子
        turkeyAdapter.fly();//它用的是鸭子的方法,但是其实真正完成动作还是用的turkey的动作累加而来
    }
}

由此可见,火鸡本来没有鸭子的quack()方法,它的fly()方法也飞不远,但是将火鸡装进适配器对象之后,适配器对象可以对火鸡进行适当的转换,让火鸡可以完成鸭子应有的quack()和fly()方法。
下面是书中适配器模式的解析图
在这里插入图片描述

这是类图

在这里插入图片描述

总结
适配器模式有良好的OO设计原则:使用对象组合,用修改的接口包装被适配者,被适配者的任何子类,都可以搭配适配器使用。
[注]这个模式是将客户和接口绑定起来,而不是和实现绑定起来。可以使用多个适配器,每个都负责转换不同组的后台类。
什么时候该用适配器模式?

当需要使用一个现有的类但是它的接口并不符合你的需要的时候。适配器可以改变接口以满足用户的期望。

下面介绍一个现实中的适配器

先介绍两个Java中的新旧两个接口
1、早期的枚举器
在这里插入图片描述
2、新版本的迭代器
在这里插入图片描述

现在我们遇到的情况是:一些旧代码暴露的是枚举器接口,但是我们希望在新代码中只用迭代器。为了让这新旧两个接口能完美衔接,我们就得设计一个适配器。

现在我们就来将枚举器接口适配为迭代器接口
在这里插入图片描述
从上图可以看出,要想适配两个接口,主要是要解决remove()方法的映射。
很不幸,枚举是一个“只读”接口,适配器无法完成一个具有实际功能的remove()方法,只能抛出一个Java设计者提前准备的运行时异常。
因此,适配器的remove()方法只能这样写:

public void remove(){
    throw new UnsupportedOperationException();
}
[注] 虽然Java已经采用了迭代器,但还是有相当多的遗留“客户代码”,依赖于枚举接口,所以利用适配器将迭代器转换成枚举,其实还是很有用的技巧。
装饰者模式和适配器模式的比较

装饰者模式可以在不改变原有代码的前提下,将对象包装进装饰者,对对象的成员进行一些装饰性的操作,即:将新行为或者责任加入给待装饰的对象。
适配器模式则是将对象包装进适配器中,适配器相当于一个转接器,将被适配对象的成员(方法或者变量)适配成另外一种形式,使其适用于另一个接口,也不需要改变原有代码。

由此可见,装饰者模式和适配器模式看起来做的事情十分相似,但是它们的意图是完全不同的。装饰者模式强调的是给被装饰者添加新的行为,而适配器模式则是强调做转换工作,让客户能兼容新的类库接口。

外观模式

外观模式提供了统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用

外观模式也是一种改变接口的模式,但是它改变接口是为了简化接口,它将一个或多个类的复杂的一面隐藏在背后,只显露出一个干净美好的外观。

从一个问题着手:
假设你想在家里弄一个家庭影院,所有东西都准备好了,现在你想看电影。现在才发现需要做很多事情:打开爆米花机、把灯光调暗、把屏幕放下来、把投影机打开、打开功放设置为DVD模式、打开DVD...

光从这看就涉及到了6个不同的类,而且看完电影还要把这一切又关掉,这简直太麻烦了。

书中这样用外观模式解决:

设计一个高层类WatchMovie,将看电影需要做的事(子系统完成的事)组合到这个外观类中,然后要看电影的时候,只需要按下WatchMovie这个按键就可以完成所有操作,而不是像开始那样花大量的时间亲手去操作那么多的不用类。
在这里插入图片描述

书中在这一节还提出了一个新的设计原则:
最少知识原则:只和你的密友谈话

当你正在设计一个系统,不管是任何对象,都要注意它所交互的对象有哪些,并注意它和这些类是如何交互的。
这个原则告诉我们:不要让太多的类耦合在一起,以免修改系统中的一部分会牵连其他部分,如果许多类之间都相互依赖,这必定是一个易碎的系统,维护成本也高。

书中这样说到:

对于任何对象,在该对象的方法内,我们只应该调用属于以下范围的方法:
1、该对象本身
2、被当作方法的参数而传进来的对象
3、此方法所创建或实例化的对象
4、对象的任何组件(与它是has-a关系的对象)
也就是说:一件事能用一个对象完成就尽量用一个,别让你的朋友去另外找人帮忙,因为你和你朋友的朋友不一定很熟,当遇到问题时,你可能不能直接联系到他

什么时候用外观模式?

当需要简化并统一一个很大的接口或者一群复杂的接口的时候,使用外观。它将用户从一个复杂的子系统中解耦。

最后小结
适配器--将一个对象包装起来以改变其接口
装饰者--将一个对象包装起来以增加新的行为和责任
外观--将一群对象包装起来以简化其接口
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值