设计模式7——结构型模式之适配器模式

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

类型:结构型模式。

适用性:

  1. 你想使用一个已经存在的类,而它的接口不符合要求。
  2. 你想创建一个可以复用的类,该类可以与其他接口不兼容的类协同工作。
  3. 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。可以使用对象适配器来适配它的父类接口。

类图:

设计模式7——结构型模式之适配器模式

参与角色:

  1. Client,与Target对象类同工作的客户端.
  2. Target,提供满足Client要求的接口.
  3. AdapterTarget的派生类,通过调用Adpatee提供的接口来实现目标接口。
  4. Adaptee,已经存在的类,需要适配,也即源接口类。

概述:

       GoF的《Design Pattern》中提出两种适配器模式,类适配器和对象适配器。类适配器是通过多重继承,来实现目标接口的。而对象适配器是通过组合来实现的。应组合/聚合复用原则,所以尽量不使用继承来实现适配器模式。

       一说起适配器,大家马上联想到的多半会是笔记本电脑的电源适配器了。这个电源适配器也确实和上文提到的适配器模式的思想是一致的。所以这里举的示例也就使用电源适配器了。

       这里以ThinkPad的某款笔记本电源适配器为例。输入电压是AC100-240V,输出电压是DC19V。例如,中国的电网电压是AC220V,但是如果出差到美国那么当地的电网电压是AC110V。像很多没有电源适配器的电器是不能在两个国家都可以使用的。但是像笔记本电脑却是可以的。这正是笔记本电脑电源适配器的作用,无论输入的是AC220V还是AC110V,经过电源适配器之后都可以转换成笔记本电脑所需要的DC19V。下面便以代码来简单描述一下电源适配器所起的作用。(AC,交流电;DC,直流电)

代码:

// 和客户进行交互的接口,即目标接口,必须提供19V电压

// 需要注意的是这里的析构函数是虚函数,这是因为保证释放子类申请的内存

  1. class CTarget  
  2. {  
  3. public:  
  4.     virtual ~CTarget(){}  
  5.   
  6.     virtual int SupportComputerVoltage() = 0;  
  7. };  

 

// 电网,提供电压接口

// 需要注意的是这里的析构函数是虚函数,这是因为保证释放子类申请的内存

  1. class CGrid  
  2. {  
  3. public:  
  4.     virtual ~CGrid(){}  
  5.     virtual int SupportVoltage() = 0;  
  6. };  


// 美国电网,提供美国电压110V,也即源接口

  1. class CUSAGrid : public CGrid  
  2. {  
  3. public:  
  4.     virtual int SupportVoltage()  
  5.     {  
  6.         return 110;  
  7.     }  
  8. };  


// 中国电网,提供中国电压220V,也是源接口

  1. class CPRCGrid : public CGrid  
  2. {  
  3. public:  
  4.     virtual int SupportVoltage()  
  5.     {  
  6.         return 220;  
  7.     }  
  8. };  


// 适配器,转换电压100-240V电压到19V

  1. class CAdapater : public CTarget  
  2. {  
  3. public:  
  4.     CAdapater(CGrid* _pGrid) : m_pGrid(_pGrid){}  
  5.   
  6.     // 实现接口,提供满足电源所需要的电压  
  7.     int SupportComputerVoltage()  
  8.     {  
  9.         if (NULL != m_pGrid)  
  10.         {  
  11.             cout<<"转换电压"<<m_pGrid->SupportVoltage()<<"V至19V"<<endl;  
  12.             return 19;  
  13.         }  
  14.   
  15.         return 0;  
  16.     }  
  17. private:  
  18.     CGrid* m_pGrid;  
  19. };  

 

// 客户端,分别针对中国电网提供的电压和美国电网提供的电压进行分析 

  1. int _tmain(int argc, _TCHAR* argv[])  
  2. {  
  3.     // 中国电网,经过适配器转换后得到适合笔记本电脑使用的电压19V  
  4.     CGrid* pGrid = new CPRCGrid();  
  5.     CTarget* pTarget = new CAdapater(pGrid);  
  6.     int nVoltage = pTarget->SupportComputerVoltage();  
  7.     if (19 != nVoltage)  
  8.     {  
  9.         cout<<"提供的电压有误,不能正常使用"<<endl;  
  10.     }  
  11.     delete pGrid;  
  12.     delete pTarget;  
  13.   
  14.     // 美国电网,经过适配器转换后得到适合笔记本电脑使用的电压19V  
  15.     pGrid = new CUSAGrid();  
  16.     pTarget = new CAdapater(pGrid);  
  17.     nVoltage = pTarget->SupportComputerVoltage();  
  18.     if (19 != nVoltage)  
  19.     {  
  20.         cout<<"提供的电压有误,不能正常使用"<<endl;  
  21.     }  
  22.     delete pGrid;  
  23.     delete pTarget;  
  24.   
  25.     return 0;  
  26. }  

 

优缺点:

  1. 优点,将已存模块和客户端进行了有效的分隔,降低了耦合性,并且最大限度地复用了代码.
  1. 缺点,过多使用适配器模式不利于代码优化.

参考资料:

  1. 《设计模式——可复用面向对象软件基础》
  1. Java与模式》
  1. 《大话设计模式》

设计模式7——结构型模式之适配器模式

上一篇:【ASP.NET Web API教程】2.4 创建Web API的帮助页面


下一篇:实战课堂:数据库高Library Cache Lock导致Hang的故障分析