设计模式-适配器模式(Adapter Pattern)
本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/
适配器模式简介
适配器模式的作用就如同现实生活中转接头的作用一样,现实生活中我们会用USB转接头把手机插到家用两孔或者三孔插座上充电,这就是一个适配的模式,在编程中我们可能会遇到现有的系统所需要的接口和新加入的类实现的接口不一样的情况,这时我们可以写一个适配器类,作为二者之间的转换器使用,从这个特点我们看到这种模式一般不在系统设计阶段使用,而是在后期扩展系统时使用。抽象的表示如下图,来自《Head First》的插图。
适配器模式的定义和基本结构
定义:将一个类的接口转化成客户期望的另一种接口,适配器可以让原本接口不兼容的类一起工作。
结构:适配器模式的实现有两种结构,类适配器(class adapter)与对象适配器(object adapter)。我们先分别介绍这两种实现方式,然后再简单介绍下两者的差别(结构体均来自《Head First》)。
对象适配器的结构:
Target:被客户端调用的类需要遵循的标准接口。
Client:客户端程序,调用的标准接口是Target接口。
Adaptee:一个接口对客户端来说是未知的类,是被适配器适配的对象。
Adapter:适配器类,是将被适配的类转换成客户端需要的Target接口类型的类,从图中可以看出,adapter类与adaptee的关联方式是组合。
一个简单的实例(java)
我们就用插座的例子来说明,假如有一个二孔插座(client类)和一个USB插头(adaptee类),很明显二孔插座的标准接口是TwoHole接口,USB用不了,所以我们要写一个USBToTHAdapter(adapter类)的类来把USB强插上去,道理很简单,下面给出实现。
首先是插座的接口类,TwoHole,只有一个方法,报告插入的插口是什么类型。
1 public interface TwoHole { 2 public abstract void info(); 3 }
然后是插座,MySocket类,会报告插进来的插口的类型。
1 public class MySocket { 2 private TwoHole th; 3 public MySocket(TwoHole th){ 4 this.th=th; 5 } 6 public void status(){ 7 System.out.print("插入了 "); 8 th.info(); 9 } 10 }
USB类,一个不同于TwoHole接口的报告自己是什么类型的方法。
1 public class USB { 2 public void putName(){ 3 System.out.println("The usb"); 4 } 5 }
USBToTHAdapter类,适配二孔插座MySocket类和USB类。
1 public class USBToTHAdapter implements TwoHole { 2 private USB usb; 3 public USBToTHAdapter(USB usb){ 4 this.usb=usb; 5 } 6 @Override 7 public void info() { 8 usb.putName(); 9 } 10 }
最后是测试代码。
1 public class Test { 2 public static void main(String[] args) { 3 MySocket s=new MySocket(new USBToTHAdapter(new USB())); 4 s.status(); 5 } 6 }
输出如下:
是不是觉得很简单。接下来看类适配器结构,先上图
类适配器的结构
注意区别,这里并没有使用组合的方式来适配adaptee而是用了多继承的方式,其他解释同上。下面同样给出代码样例,由于java不支持多继承,所以用C++来写,例子同上(因为代码很简单我就不分写.h和.cpp文件了)。
一个简单的实例(C++)
1 #include<iostream> 2 using std::cout; 3 using std::endl; 4 5 class TwoHole{ 6 public: 7 virtual void info(){cout << "null"<<endl;} 8 }; 9 10 class MySocket{ 11 private: 12 TwoHole* th; 13 public: 14 MySocket(TwoHole* th){ this->th = th; } 15 void status(); 16 }; 17 18 void MySocket::status(){ 19 cout << "插入了"; 20 th->info(); 21 } 22 23 class USB{ 24 public: 25 void putName(){ cout << "The usb"<<endl; } 26 }; 27 28 class USBToTHAdapter :public USB, public TwoHole{ 29 public: 30 void info(){ putName(); } 31 }; 32 33 int main(){ 34 MySocket s = MySocket(new USBToTHAdapter()); 35 s.status(); 36 return 0; 37 }
输出输出如下:
关于类适配器和对象适配器之间的区别:
1,类适配器使用了多继承,而对象适配器使用的是组合。
2,类适配器由于使用了继承,理论上效率会比对象适配器快一丢丢,而且能对被适配者进行方便的重写。
3,对象适配器,使用的是组合的方式,能适配被适配对象和其子类,灵活性更好(前提是子类对象不加入新的行为)。
其实,说白了就是一个具有了组合带来的优点,一个具有了继承带来的优点。具体使用哪个任凭大家喜欢吧,楼主更倾向于组合方式,因为实在不喜欢多继承_(:зゝ∠)_。
适配器模式和装饰者模式的区别
两者都用到了包装这一方法,所以看上去会有点相似,但是他们两种设计模式的目的是完全不一样的。
适配器模式的目的是为了改变接口,把任意接口改变成我们需要的接口,重点在改变。
装饰者模式的目的是为了增加特性或者功能,是为了丰富被装饰者,重点在与 增加。
适配器模式到此结束♪(^∇^*)。
参考资料:《Head First 设计模式》。