设计模式:适配器模式
适配器模式(Adapter Pattern),别名Wrapper。
适配器模式分为两种:对象适配器模式;类适配器模式。
理解类图需要知道的一些基本名词和符号:UML类图的几个名词及对应符号
对象适配器模式
Client想要调用某个具体Adaptee的方法,来实现某个功能A。最直接的做法是将Adaptee作为参数传入,然后让Client创建一个方法去调用。
如果Client也想调用另一个具体Adaptee的方法,也实现某个功能A。此时再将Adaptee作为参数传入,然后让Client创建一个方法去调用。
……
当Client想调用一百个类(甚至不止这些)的方法,来实现同一个功能A,那不就得添加一百个方法?真这么做,这个类就太臃肿了。并且,在某个具体的系统,极大部分的方法都不会被用到。
那么,提取出一个接口(Target),并让Client调用这个接口的方法不就行了吗?这样Client就不需要为某个具体的类添加方法。 问题就这样解决了,但还没结束。
既然有一个接口,那么不就意味着每个具体Adaptee都要实现这个接口了么?最理想的情况是,这些类刚好那个被调用的方法的方法名是相同的,直接加个implements xxx
就行了(前提是你被允许修改这些类的代码)。
当然,现实并不是这么理想。它们的方法名往往不同,而你也不被允许修改它们的代码,更不用说去这些类里面去实现这些方法。
那TM怎么办?
先把已知条件整理出来:已知Client会调用Target接口的某个方法,但是具体类的方法名与接口的方法名不匹配,不能让具体类实现Target接口并传入Client。
问:如何让Client只调用Target接口的方法而间接地调用到具体类的方法?
答:创建一个类(MyAdapter
),这个类里有一个方法,其方法名与Target定义的方法名完全一致(假设为sayHello()
)。接着将具体的类(假设为 RudeMan
)传入这个新建的类,在 sayHello()
里面调用具体类的方法(假设为 sayFuck()
)。这就相当于给RudeMan的sayFuck()方法包装了另一个名字。
既然MyAdapter的方法名与Target定义的一致,那么就可以加上implements xxx
了。
public class MyAdapter implements Target {
private RudeMan rudeMan = null;
public MyAdapter(RudeMan man) {
rudeMan = man;
}
public void sayHello() {
rudeMan.sayFuck();
}
}
这时,MyAdapter类相当于是一个转接口,将具体类RudeMan的具体方法包装成另一个名字。并且MyAdapter实现了Target接口,可以传入Client供其调用。
类适配器模式
由于类适配器模式有时需要多重继承,而JAVA不支持,所以通常不使用类适配器模式。
对于类适配器模式的方式,你会看到这样的形式:
public class Adapter extends Adaptee implements Target {
……
}
这里extends是为了调用具体类Adaptee的方法,而implements是为了让Client调用已被Target定义好的方法。
从UML类图角度来对比两者
对象适配器模式:基本聚合
类适配器模式:泛化