阿Sam的设计模式学习笔记---- Adapter模式

原创 2007年09月18日 09:37:00

1,  功能:

将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

2,  基本思想

通过一个适配器(Adapter),在已有的类(Adaptee)和客户(Client)之间进行协调,从而达到兼容的目标。

Adapter模式中,有两种适配方法:类适配器和对象适配器。类适配器通过多重继承实现,对象适配器通过委托来实现。

OOP中的一个重要思想是:尽量使用聚合而不是继承。

一、类适配合对象适配的概念:

1)类适配

Ÿ   类适配器用一个具体的Adapter类对AdapteeTarget进行匹配。结果导致当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作。

Ÿ   使得Adapter可以重定义Adaptee的部分行为,因为AdapterAdaptee的一个子类。

Ÿ   仅仅引入了一个对象,并不需要额外的指针以间接得到adaptee

2)对象适配器

Ÿ   允许一个Adapter与多个Adaptee(即Adaptee本身以及它的所有子类)同时工作。Adapter也可以一次给所有的Adaptee添加功能。

Ÿ   使得重定义Adaptee的行为比较困难。这需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。

二、类适配和对象适配的比较 (参考:http://blog.csdn.net/qinysong/category/225891.aspx

1)  AdapterAdatpee的特殊性要求方面:

  • 类适配:AdapterAdaptee的子类,所以Adapter可以根据需要重新定义Adaptee的个别方法。
  • 对象适配:Adapter不是Adaptee的子类,所以如果AdapterAdaptee中的个别方法有特殊的需要,就要新建Adaptee的子类,而让Adapter使用这个子类(即Adapter组合这个子类的对象)。

2)  Adaptee的类层次方面:

  • 类适配:AdapterAdaptee的子类,编译后就不能再更换所实现的父类Adaptee,因此如果有一个Adaptee的类层次结构,就要相应的有一个Adapter的类层次结构(AdapterTargetAdaptee的子类,所以多一个Adapter,就需要有一个TargetAdaptee做它的父类),且新扩展Adaptee时很不方便。
  • 对象适配:Adapter不是Adaptee的子类,而是通过使用的方式,所以只要Adaptee是相同类型的(拥有相同的父类),就可以在运行时更换Adapter所使用的Adaptee。这样就可以容易扩展新的Adaptee

3,  适用情况

Ÿ   想使用一个已经存在的类,而它的接口不符合实际需求。

Ÿ   想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。

Ÿ   (仅适用于对象Adapter)想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配其父类接口。

4,  结构

类适配器的结构

对象匹配器的结构

 

5,  举例

借用网上一位牛人的C#的变压器例子,我用C++翻译了一下。

变压器是一种Adapter,它把一种电压转换成另外一种电压。美国的生活用电电压是110V,中国是220V。如果在老美那里买了电器,回国后就要加个变压器才可以用了。

1)  类适配器

// Target Class
class ElectricUtils
{
public
:
  virtual 
void TwoPhaseFaucet() = 0
;
  virtual 
void Power_110V() = 0
;
};

// Adaptee Class

class ChinesePower
{
public
:
  ChinesePower():volt(
220)
                {cout
<<"The Voltage in China is " << "volt" <<
 endl;}
  
int getVolt() const
;
  
void setVolt(int v) {volt =
 v;}
protected
:
  
int
 volt;
};
int ChinesePower::getVolt() const

{
  
return volt;
}

class Transformer : public ElectricUtils,  // C++中应该用public继承Target

                    private ChinesePower  // 用private继承Adaptee
{
public
:
  
void TwoPhaseFaucet(){cout<<"The Two-Phase-Facucet provides transformation from 3 to 2 phase"<<
endl;}
  
void Power_110V(){volt=getVolt()/2;cout<<"Power transformer. The output is "<<volt<<
endl;}
};
int
 main()
{
  ElectricUtils 
* dev = new
 Transformer();
  dev
->
Power_110V();
  dev
->
TwoPhaseFaucet();
  
return 0
;
}

 

2)  对象适配器

对象适配器中的ElectricUtilsTarget)类和ChinaPowerAdaptee)类都不需要变换,变化的是Adapter“组合”它们的方式。所以这里只需要修改一下Adapter类。

class Transformer : public ElectricUtils   // C++中应该用public继承Target
{
public:
  Transformer(){power 
= new ChinesePower();}
  
~Transformer(){if (power != NULL) delete power;}
  
void TwoPhaseFaucet(){cout<<"The Two-Phase-Facucet provide transformation from 3 phase to 2 phase"<<endl;}
  
void Power_110V(){power->setVolt(power->getVolt()/2);cout<<"Power transformer. The output is "<<power->getVolt()<<endl;}
private:
  ChinesePower 
* power;
};

 

6,  可插入的Adapter

用书上的例子来说,有一个图形化显示树状结构的TreeDisplay窗口组件,如果它仅在一个应用中使用,我们可能要求它所显示的对象有特殊的接口,也就是说这些被显示的对象应该是Tree的子类。但是不同的应用不可能都要使用我们的抽象类Tree,它们可能会自己定义自己的树结构,而不同的树结构会有不同的接口。

例如在一个目录层次结构中,通过调用GetSubdirectories操作来访问子目录,然而在继承式层次结构中,相应的操作是GetSubclasses。这两种应用结构中使用的接口不同,所以TreeDisplay组件需要有接口适配的功能,使得它能够显示这两种结构。

关于可插入的适配器,首先要为Adaptee找到一个“窄”接口,即可用于适配的最小操作集。因为包含较少操作的窄接口相对包含较多操作的宽接口比较容易进行匹配。对TreeDisplay而言,最小接口集合仅包含两个操作:一个操作用于在层次结构中表示一个节点,另一个操作返回该节点的子节点。接下来,有三种方式可实现这个窄接口。

一、抽象接口操作:

TreeDisplay中定义窄Adaptee接口相应的抽象操作。由子类来实现这些抽象操作并匹配具体的树结构的对象。例如,DirectoryTreeDisplay通过访问目录结构实现这些窄接口操作。下图中,TreeDisplayTarget,直接被客户使用。FileSystemEntityClassHiracheyEntityAdaptee,是我们希望“借用”它们某些实现的类。DirectoryTreeDisplayClassTreeDisplay继承TreeDisplay,它们是Adapter,实现了在Target中定义的Adaptee的最窄接口。

二、代理对象:

TreeDisplay将访问树结构的请求转发到代理对象,TreeDisplay的客户进行一些选择,并将这些选择提供给代理对象,这样客户可以对适配加以控制。

比较上面两图,可以看出,代理对象方法其实就是将TreeDisplayTarget)中,将“窄”接口剥离出去,形成一个代理,并且在TreeDisplay中定义一个获取代理接口的方法。其他方面都和抽象接口方法一样。

C++中,需要代理的显示接口定义,上图中的TreeAccessorDelegate就是一个纯虚类作为一个接口。然后使用继承机制,真正的代理(DirectoryBrowser)实现接口。

三、参数化的适配器

Smalltalk中支持可插入适配器的方法是,用一个或多个模块对适配器进行参数化。由于鄙人对Smalltalk一点儿都不了解,所以这里略过J

7,  总结

(1)       AdapterAdaptee的接口与Target的接口进行匹配的工作量取决于Target接口与Adaptee接口的相似程度。

(2)       使用C++实现类适配器时,Adapter类应该采用public方式继承Target类,用private方式继承Adaptee类。

(3)       当其他的类使用一个类时,如果所需的假定条件越少,这个类就更具可复用性,即Adapter是可插入的。为使一个Adapter可插入,就必须要为Adaptee找到一个窄接口,即可用于适配的最小操作集。

相关文章推荐

设计模式笔记1——适配器Adapter模式

1. 根据意图对模式分类   2. 接口型模式介绍   2.1. 抽象类与接口的区别 ①一个类能实现多个接口,但只能继承一个抽象类 ②抽象类可以包含具体方法,但是接口只能都是抽象方法 ③抽象类可以声明...

设计模式笔记-Adapter模式

适配器模式,顾名思义,适配两个不同的东西!实事上,它的用处更多的确实是用在:当前项目开发,用到第三方的库,我们自己的项目接口已经设计好,但它与第三方库的接口不一致,为了这些接口不兼容的类能一起工作,就...

设计模式Adapter模式

  • 2011年06月02日 23:16
  • 820B
  • 下载

Java学习之道:java设计模式—Adapter模式

1、核心意图:     将一个类的接口转换成客户希望的另外一个接口,从而使得原本由于接口不兼容而不能一起工作的类可以一起工作。 该模式的目标是通过一个代理(这里是Adapter),在原来的类(Ad...

菜鸟学设计模式系列笔记之适配器模式(adapter模式)

Adapter模式的应用场景: 扩展应用时——想要修改一个投产中的接口时 1、用于解决正在服役的项目问题,在详细设计阶段一般不予考虑 2、补偿模式,用于解决接口不相容问题,通过把非本系统接口的对象包装...

[设计模式笔记]二. 结构型模式--7.Adapter模式(适配器模式)(一)

结构型模式 Adapter模式(适配器模式) 一. 意图 将一个类的接口转换成客户希望的另外一个接口. Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作 ...
  • cay22
  • cay22
  • 2013年09月12日 21:39
  • 1228

[设计模式笔记]二. 结构型模式--7.Adapter模式(适配器模式)(二)

场景 有某设备需要这样的功能: 开灯/关灯, 开风扇/关风扇. 像这种开关动作, 一般使用I/O卡控制. 假设I/O卡有很多种型号. 这里可以使用Simaple Factory模式 角色 ...
  • cay22
  • cay22
  • 2013年09月12日 21:44
  • 1247

【设计模式学习笔记】Adapter

Adapter(适配器)模式解决的是接口兼容的问题。 举现实中的例子来说,墙上只提供了三相的插座,我们的电器却使用了二相的插头。我们要怎样使电器可以工作?很简单,买一个二相转三相的适配器就可以了。 ...
  • ruozeng
  • ruozeng
  • 2011年09月27日 16:30
  • 341

设计模式》学习笔记--适配器Adapter

欢迎转载,请注明出处。 1、核心意图:     将一个类的接口转换成客户希望的另外一个接口,从而使得原本由于接口不兼容而不能一起工作的类可以一起工作。 该模式的目标是通过一个代理(这里是...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:阿Sam的设计模式学习笔记---- Adapter模式
举报原因:
原因补充:

(最多只允许输入30个字)