这次是两个模式,适配器模式 & 外观模式,为什么一下子写两个模式呢?来想想上次讲的装饰模式的功能是什么?我个人感觉:他将一个有相同超类的类进行包装,从而使其获得新的功能;而在看完适配器模式和外观模式之后,我觉得他们三者很像:一个通过包装增加功能,另一个通过包装改变接口,最后一个通过包装简化接口。这样一看可能会觉得比较绕,没事,接下来我会理清这3者之间的相似与不同。依然会用到那个例子,你问我那个?看看前面几章就知道了…
适配器模式
适配器模式:将一个类的接口,转换成客户期望的另一个接口。让原本接口不兼容的类可以合作。
常见的适配器
适配器这个概念应该很好理解,现实中也有很常见的例子,比如各种数据线的转接头。更具体的说:电脑上都有usb插口,这就是一个接口,而任何实现了这个接口的对象都可以和电脑进行连接,比如U盘。那么如果手机想和电脑连接怎么办呢,手机也没有实现usb接口啊(没有符合usb接口的插头),所以手机没法直接和电脑连接(手机没法像U盘那样直接插在电脑上)。
你可能会说,这不是有数据线么? 对,数据线就可以当做是适配器,一头是usb插头连接电脑,另一头是符合你的手机的接头,他完成了将两个不同的头转换的功能。那么将其抽象,手机和电脑就是 原有类 和 目标类,(哪个是哪个无所谓,只需知道他们的接口不同就好了),而数据线就是 适配器类。
请假的厨师
天气无比的热,这40多度的天气真不是人能承受的,尤其是在厨房啊!所以啊,Pizza店的厨师中暑无法工作(真是个悲伤的故事)可是店依旧得开下去,怎么办呢?只能先随便找个人顶上去!于是你去隔壁的酒楼花了不少money“借”了个厨师,让他先顶上。人是借到了,但是现在来不及给他培训了怎么办呢?由于比较紧急,“那就安你会做的东西来做!”你这样对厨师说道。他一拍胸脯“这事包我身上了”(最近一有空就在看武林外传…)
先来对比一下这两位厨师的类吧(突然感觉能把人抽象好可怕…)
1 | /** |
可以看到 以上两个厨师里的方法完完全全不一样,在现在的程序体系中使用ChefPizza的方法,新的ChefTongfu没有,所以需要一个适配器来进行转接。下面就看看这个适配器该怎么写,看完你会发现原来这么简单。。。
1 | public class Adapter implements PizzaChef{ |
首先这个适配器实现了目标接口,理解上就是:你这个适配器是将其他对象转换成 满足PizzaChef 这个接口的东西的,然后是将什么东西转换呢?看构造方法,在上面的构造方法中,接受了ChefTongfu类型的对象,将其保存。然后在PizzaChef接口所需实现的方法中,调用ChefTongfu中对应要转换的方法。由于其实现了PizzaChef接口,所以对外那些面向接口编程的代码中,Adapter对象可以当做是一个PizzaChef来使用,他的具体方法一一对应到了他构造方法接受到的对象的那些方法。
说了这么多绕口的,下面直接写一下具体的使用也比较清晰
1 | public class Simulator{ |
现在已经成功的让新来的厨师去工作了,虽然他做出的东西可能并不是你想要的,但是。。。他至少顶上了这一天不是么?至于效果就不要多追究了。。。
最后看一下适配器模式的类图
双向适配器
上面写的这些,从功能上来说,是单向适配器,何为单向?就字面理解,他只能讲一种类转换成目标类。那双向适配器就很容易的理解了,可以支持双向转换。在深透理解了单向适配器后就很容易去写了。
1 | public interface TongfuChef{ |
因为现在是双向适配器,所以所谓的目标类又多了一个,我就给原先的ChefTongfu 做了一个接口TongfuChef,取名字反了一下(这个是我的错,我想不出有什么名字了,所以在看的时候一定一定要分清是 接口 还是 类)。然后在双向适配器TwoWayAdapter中,我写了3个构造方法,第一个是在单向适配器中已经解释过了的,第二个同理,反向的一个构造方法。这里我解释一下第三个,他可以同时担任两者的适配器。有人可能会说,有了第一个和第二个构造方法,第三个还有必要么? 我举个例子:有一条单行道,他既可以从左往右(第一种构造),也可以从右往左(第二种构造),但无论是哪一种,他同一时刻只能担任一种角色,若要给2个同时做适配,那就需要多创建一个实例。我突发奇想,同时接受2个参数的话,就可以同时当做2种对象来用了,这样很cooooooooool不是么。。(虽然我不是很推荐这样干。。)在最后写的是2种类具体的转换,详细的不多赘述,在单向中已经说过了。
至于双相适配器的类图嘛、、大家想想应该是什么样子的呢?探索一下吧
java中的适配器
在java中如果使用过集合set,那么肯定对Iterator不陌生,迭代器。他可以方便的让你遍历集合类型中的元素。但是,在Iterator之前还有一个Enumeration枚举器这个东西,不过已经很久不用了。但是如果要处理一些历史遗留代码(一些老程序)可能他用的就是enum,这时候就可以自己写一个适配器了。现在已知Iterator接口中有hasNext(),next(),remove()方法、在Enumeration中有hasMoreElements(),nextElement()方法,那么试试看如何写一个适配器呢?
1 | public class EnumerationIterator implements Iterator{ |
在写适配器的时候最重要的就是在两者之间找到映射关系,这里看上面的remove方法,由于在enum中没有可以转换的方法,但是空着不写也不行(不写的话就不会执行任何东西,客户会觉得是代码哪里出错了“我按了啊?怎么没反应呢?”)所以在这里抛出一个运行时的异常,通知外部这里的情况。
*对象适配器和类适配器
以上说的全部都是对象适配器,类适配器涉及到了多重继承,而在java中是没有多种继承的,所以我并不很清楚这到底是什么。不过根据参考资料所说,类适配器与对象适配器唯一的区别就是,类适配器继承了Target 和 Adaptee。而对象适配器利用组合的方法来将请求传递给别适配者。如果有兴趣的可自己去查阅一下这方面的资料。
外观模式
好了 外观严意义上来说是另一个模式吧。那下周更吧。。
三方会谈
这里主要是 写 装饰者 外观 适配器 三个的比较下周再说