我感觉我现在写的东西就像小孩子玩玩具一样,没怎么深入,数据结构与算法都忘得差不多了,不知道多久有时间再回去观望一下啊!!
适配器模式(Adapter)
将一个类的接口转换成客户希望的另外一个接口。(这里说的接口不仅仅是我们Java中的Interface本意,而是说本质中的不匹配)Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
别人问你什么是适配器模式的时候,你就可以说上面这段话。
比如我这里有一个关于接口的继承树(从一个接口通过不断的实现很多子代)
我们一般定义类的时候都是通过接口再外表现,而内在为子类的实例对象。
如果现在在这个继承树的外边有一个和当前继承树实例对象的功能有类似的或者是增强的。那么现在当我们用同一个接口调用不同继承树里面的功能的时候就会用到适配器模式。当然不是一定要有接口才能用适配器。下面笔者就会讲解到。
在GoF的设计模式中,对适配器模式讲了两种类型,类适配器模式 和 对象适配器模式。
由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配,而C#、VB.NET、JAVA等语言都不支持多继承(C++支持),也就是一个类只有一个父类,所以这里主要讲的是对象适配器。
用到适配器模式有两种情况:
第一种是调用不同继承树里面的对象功能。用适配器时,这是一种亡羊补牢的过程,是无赖之举。主要是由于当时没有设计好的原因。
第二种是调用第三方开发组件,第三方组件与我们的接口不符合时。
没有接口时,用到适配器模式:
public class Target {
/*
* 这是客户现在用的类
*/
public void request(){
System.out.println("普通请求!!");
}
}
public class Adaptee {
/*
* 这是客户以前用的类
* 需要适配的类
*/
public void specificRequest(){
System.out.println("特殊请求!");
}
}
public class Adapter extends Target{
//建立一个私有的Adaptee对象
private Adaptee adaptee=new Adaptee();
@Override
public void request() {
//这样就能够把表面上调用Request()方法变成实际调用SpecificRequest()
adaptee.specificRequest();
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
Target target=new Adapter();
//调用的就是 Target的Request
target.request();
}
}
这样,客户端代码就可以统一调用同一接口,可以让代码调用更简单、更直接、更紧凑。
其实使用适配器模式也是无奈之举,有点亡羊补牢的感觉,没办法呀,是个软件就有维护的一天,维护就有可能会因不同的开发人员,不同的产品、不同的厂家而造成功能类似而接口不同的情况,此时就是适配器模式大展拳脚的时候了。
也就是说一般都是用在软件开发后期或者维护期再考虑使用它,没必要在设计阶段而把功能类似的类设计不同的接口。
而且在公司里面类和方法命名应该有规范,一般前期都设计好了的,如果哪儿程序员真的把功能类似的类写成不同的接口,那么一般都是考虑重构成统一接口,而不是考虑用适配器。
如果要用基本上都是双发都不太容易修改的时候再使用适配器模式。而不是一有不同时就使用它。
如果要在设计之初就使用适配器模式,那么这种情况就是公司的产品考虑使用第三方开发组件,而这个组件的接口与我们自己的系统接口是不同的,而我们也完全没有必要为了迎合它而改动自己的接口。
此时尽管是在开发的设计阶段,也是可以考虑用适配器模式来解决接口不同的问题。
篮球翻译适配器
在很多年前姚明被问到NBA和CBA有什么不同的时候,姚明幽默的回答道,在NBA我需要翻译,而在CBA我不需要。
需求:教练叫暂停时给后卫、中锋、前锋分配进攻和防守任务的代码。
不用适配器时,平常我们写代码可能是这样:
/*
* 定义球员抽象类,有进攻和防守的方法
*/
public abstract class Player {
protected String name;
public Player(String name){
this.name=name;
}
public abstract void attack();
public abstract void defense();
}
/*
* 前锋
*/
public class Forwards extends Player{
public Forwards(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("前锋 进攻!!"+name);
}
@Override
public void defense() {
System.out.println("前锋 防守!!"+name);
}
}
/*
* 中锋
*/
public class Center extends Player {
public Center(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("中锋 进攻!!"+name);
}
@Override
public void defense() {
System.out.println("中锋 防守!!"+name);
}
}
/*
* 后卫
*/
public class Guards extends Player {
public Guards(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("后卫 进攻!!"+name);
}
@Override
public void defense() {
System.out.println("后卫 防守!!"+name);
}
}
/*
* 客户端代码
*/
public class Client {
public static void main(String[] args) {
Player aPlayer=new Forwards("FireLang");
aPlayer.attack();
Player bPlayer=new Guards("LangShen");
bPlayer.attack();
Player ym=new Center("姚明");
ym.attack();
ym.defense();
}
}
/*
结果显示:
前锋 进攻!!FireLang
后卫 进攻!!LangShen
中锋 进攻!!姚明
中锋 防守!!姚明
*/
注意:姚明刚到NBA,虽然他身高够高,球技够好,但是,他那是还不懂英语,也就是说,他听不懂教练的战术安排,Attack 和 Defense是什么意思都不知道。这样写是有问题的。
事实上当时是如何解决这个矛盾的??
“姚明说:’我需要翻译’”。姚明是外籍中锋,需要有翻译者类“适配”。
/*
* 外籍中锋
*/
public class ForeignCenter {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void attackByForeign(){
System.out.println("外籍球员 进攻 !"+name);
}
public void defenseByForeign(){
System.out.println("外籍球员 防守!"+name);
}
}
/*
* 翻译者类
*/
public class Translator extends Player {
private ForeignCenter wjym=new ForeignCenter();
public Translator(String name) {
super(name);
wjym.setName(name);
}
@Override
public void attack() {
//翻译者将 attack 翻译为 进攻
wjym.attackByForeign();
}
@Override
public void defense() {
//翻译者将 defense 翻译为防守
wjym.defenseByForeign();
}
}
/*
* 改写客户端代码
*/
public class Client {
public static void main(String[] args) {
Player aPlayer=new Forwards("FireLang");
aPlayer.attack();
Player bPlayer=new Guards("LangShen");
bPlayer.attack();
Player ym= new Translator("姚明");
//翻译者告诉姚明,教练要求你既要 ‘进攻’ 又要 ‘防守’
ym.attack();
ym.defense();
}
}
/*
输出结果:
前锋 进攻!!FireLang
后卫 进攻!!LangShen
外籍球员 进攻 !姚明
外籍球员 防守!姚明
*/
扁鹊的医术
当年,魏文王文名医扁鹊说:‘你们家兄弟三人,都精于医术,到底哪儿一位最好呢?’ 扁鹊答:‘长兄最好,中兄次之,我最差。’ 文王再问:‘那么为什么你最出名??’扁鹊答:
‘长兄治病,是治病于病发作之前。’由于一般人不知道他事先能铲除病因,所以他的名气无法传出去:中兄治病,是治病于病情初起时。一般人以为他只能治轻微的小病,所以他的
名气只及本乡里。而我是治病于病情严重之时。一般人都看到我在经脉上穿针管放血、在皮肤上敷药等大手术,所以大家都以为我的医术高明,名气因此响遍全国。‘这个故事能够说明什么??
也就是说:如果能够事先预防接口不同的问题,不匹配问题就不会发生;在有小的接口不统一问题发生时,及时重构,问题不至于扩大;只有碰到无法改变原有设计和代码的情况时,才考虑适配。事后控制不如事前控制。
如果能事前控制,又何必事后再去弥补呢??适配器模式当然是个好模式,如果无视它的应用场合而盲目使用,其实是本末倒置了。
很多原句摘自《大话设计模式》 下载设计模式