写在前面

为方便读者,本文已添加至索引

Adapter(适配器)模式主要解决接口不匹配的问题。为此,让我们要回到最初Builder模式创建平行世界时,白雪公主和小霍比特人的谜之相遇。在这个世界里,我们暂时有见到两个种族Human和Hobbit。种族不同所产生的最大区别是什么?当然个头是一部分,也仅仅是一部分而已。毕竟Human里也有怎么长都才那么高的吧:P。这里要说的区别是:他们的语言不通。是的,白雪公主一开始根本就听不懂小霍比特人在说什么。

在这个平行世界中,所有的Human通用语是中文,(为什么是中文!因为全世界都在学中国话。)包括白雪公主(你知道我是一定不会忠于原著的)。而霍比特人,我们假定他们用的是霍比特语。所以大家在表达“打招呼”的意思时,用的是完全不同的语言:

 class Human {
public:
// ... other action ...
virtual void hello() { cout << "你好!" << endl; }
} class Hobbit {
public:
// ... other action ...
virtual void hohobi() { cout << "HOhoBI*" << endl; }
}

那后来白雪公主又是怎么与小霍比特人们幸福快乐地生活在一起了呢?这里要提的是7个小霍比特人中,有一位博学的老者theWise,他曾在人类社会中生活了大半辈子,他曾任职于『跨种族文化研究协会』Cross-Racial Culture(CRC)。那段难忘的研究经历让他对Human的文化也是相当熟悉:

 class CRC: public Human, public Hobbit {
public:
// ... other action ...
virtual void hohobi() { hello(); }
}

可以看到,CRC其实就是一个适配器,更具体的说,是类适配器。(适配器模式有两种版本:类适配器对象适配器,下文会讲到)它使用多重继承对一个接口与另一个接口进行匹配,见示例部分的说明。大家可以想想现实生活中,我们是不是还见过许多类似适配器的设计?让我们来看看适配器模式的相关要点。

要点梳理

  • 目的分类

    • 类对象结构型模式
  • 范围准则
    • 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
    • 类(该模式处理类和子类之间的关系,这些关系通过继承建立,是静态的,在编译时刻便确定下来了)
  • 主要功能
    • 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
  • 适用情况
    • 我们想使用一个已经存在的类,而它的接口不符合我们的需求
    • 我们想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口

      可能不一定兼容的类)协同工作

  • 参与部分
    • Target:定义Client使用的与特定领域相关的接口
    • Client:与符合Target接口的对象协同
    • Adaptee:定义一个已经存在的接口,这个接口需要适配
    • Adapter:对Adaptee的接口与Target接口进行适配
  • 协作过程
    • Client在Adapter实例上调用一些操作。接着适配器调用Adaptee的操作实现这个请求
  • UML图

类适配器

对象适配器

示例分析 - 跨越语言的障碍

当美丽的白雪公主遇到一个可爱的小霍比特人时,她向他打招呼:

 Human theWhitePrincess = new Human();
Hobbit theLovely = new Hobbit();
theWhitePrincess.hello(); //你好!

但是小霍比特人理解不了。在他的世界里,hohobi()才是打招呼的方式。而Human是没有hohobi()的,于是白雪公主沟通陷入了尴尬的状态。好在theWise及时的来了:

 Hobbit theWise = new CRC();
theWise.hohobi(); //你好!

于是白雪公主和其他小霍比特人通过theWise都能互相理解了。借用下面这个图我们可以理解下:

跨种族文化研究协会就是为了实现种族之间文化交流沟通没有障碍而生。它在这个场景下,就起到了适配器的作用,通过将Hobbit人打招呼的接口hobbit()转化成Human打招呼的接口hello进行输出。

但是世界的创造者时の魔导士也注意到了这件事。他并不希望以后的日子里,白雪公主都要在theWise的陪同下才能与其他人沟通,太不方便了对不对。于是他采用了童话世界里最常出现的谜之物:霍比特仙果:一个能吃了后能让人类获得理解霍比特人的神奇果实。从本质上来看,吃过果子后,属于新的类别:HumanPlus

 class HumanPlus : public Human {
public:
// ... other action ...
virtual void hello() { _hobbitSprite->hohobi(); }
private:
Hobbit* _hobbitSprite;
}

它的最大特色是,自己维护了一个Hobbit类的指针。利用_hobbitSprite对象达到适配器的目的,这就是另一种对象适配器的模式。让我们来看图:

总之,白雪公主顺利地融入到小霍比特人的生活中。

特点总结

在此,我们可以总结下类适配器对象适配器各自的特点:

  1. 类适配器用一个具体的Adapter类对Adaptee和Target进行匹配。但如此一来,类Adapter将不能匹配Adaptee类的所有子类了。
  2. 对于类适配器,Adapter类是Adaptee类的子类,因此可以重定义Adaptee类的部分行为。
  3. 对于类适配器,仅仅引入一个对象,并不需要额外的指针以间接得到adaptee
  4. 对象适配器则允许一个Adapter与多个Adaptee同时工作,它也可以一次给所有的Adaptee添加额外的功能。
  5. 对于对象适配器,重定义Adaptee的行为本身比较困难。

我们需要注意的一些问题:

  1. 对Adaptee的接口与Target的接口进行匹配的工作量各个Adapter可能不一样,主要取决于Target接口与Adaptee接口的相似程度。
  2. 当其他的类使用一个类时,如果所需的假定条件越少,这个类就更具可复用性。如果将接口匹配构建为一个类,就不需要假定对其他的类可见的是一个相同的接口。也就是说,接口匹配使得我们可以将自己的类加入到一些现有的系统中去,而这些系统对这个类的接口可能会有所不同。
  3. 使用适配器的一个潜在问题是,它们不对所有的客户都透明。被适配的对象不再兼容Adaptee的接口,因此并不是所有Adaptee对象可以被使用的。这种情况下,一个双向的适配器就很有必要了。一般可以使用多重继承来实现这一目的,尤其是两个类接口差异程度较大时。

写在最后

今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!

[学习笔记]设计模式之Adapter的更多相关文章

  1. [学习笔记]设计模式之Chain of Responsibility

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 最近时间比较紧,所以发文的速度相对较慢了.但是看到园子里有很多朋友对设计模式感兴趣,我感觉很高兴,能够和大家一起学习这些知识. 之前的 ...

  2. [学习笔记]设计模式之Abstract Factory

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在上篇笔记Builder设计模式中,时の魔导士祭出了自己的WorldCreator.尽管它因此能创造出一个有山有树有房子的世界,但是白 ...

  3. [学习笔记]设计模式之Builder

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 作为一个新入职的魔导士呢,哦不,是程序员,我以为并没有太多机会去设计项目的软件架构.但是,工作一段时间之后,自己渐渐意识到,哪怕是自己 ...

  4. [学习笔记]设计模式之Bridge

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 “魔镜啊魔镜,谁是这个世界上最美丽的人?”月光中,一个低沉的声音回荡在女王的卧室.“是美丽的白雪公主,她正和小霍比特人们幸福快乐地生活 ...

  5. [学习笔记]设计模式之Prototype

    写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在笔记Builder模式中,我们曾见到了最初用于创建平行世界的函数createWorld,并且它是Mage类的成员函数(毕竟是专属于魔 ...

  6. [学习笔记]设计模式之Command

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...

  7. [学习笔记]设计模式之Composite

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在Composite(组合)模式中,用户可以使用多个简单的组件以形成较大的组件,而这些组件还可能进一步组合成更大的.它重要的特性是能够 ...

  8. [学习笔记]设计模式之Proxy

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 “魔镜啊魔镜,谁是这个世界上最美丽的人?” 每到晚上,女王都会问魔镜相同的问题(见Decorator模式).这是她还曾身为女巫时留下的 ...

  9. [学习笔记]设计模式之Flyweight

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 Flyweight(享元)模式运用共享技术,可以有效地支持大量细粒度的对象.今天我们会去参观小霍比特人们的酿酒工坊……等等,不是享元模 ...

随机推荐

  1. keybd_event函数用法

    转自不用winio,直接达到驱动级模拟键盘效果的keybd_event函数用法 键盘模拟技术是在编写游戏外挂时经常使用的技术.但是由于很多游戏采用了directinput的方式,使得发送的一般键盘消息 ...

  2. IAR Embedded Workbench 破解方法+工具+授权文件

    转自IAR Embedded Workbench 破解方法+工具+授权文件 本文重点阐述了如何手动爆破 IAR EWARM 6.x以及生成License,目的一是和大家分享下,二是自己记录下过程,以便 ...

  3. 控制台程序使用MFC类的方法

    (unresolved external symbol __endthreadex解决办法) 1.新建控制台程序: 2.添加源代码如下: #include <afx.h> #include ...

  4. 【java并发】线程同步工具Semaphore的使用

    Semaphore通常用于限制可以访问某些资源(物理或逻辑的)的线程数目,我们可以自己设定最大访问量.它有两个很常用的方法是acquire()和release(),分别是获得许可和释放许可.  官方J ...

  5. Android用户界面 UI组件--TextView及其子类(三) EditView以及各种Span文字样式讲解

    EditView和TextView的用法差不多,只是文字可编辑 小技巧: 设置EditText隐藏键盘  setInputType(0); 设置EditText不被输入法遮盖  getWindow() ...

  6. [LeetCode] 303. Range Sum Query - Immutable (Easy)

    303. Range Sum Query - Immutable class NumArray { private: vector<int> v; public: NumArray(vec ...

  7. What exactly is the difference between WndProc and DefaultWndProc?

    Control.DefWndProc Sends the specified message to the default window procedure. 参数说明:m:The Windows M ...

  8. hadoop2.2编程:mapreduce编程之二次排序

    mr自带的例子中的源码SecondarySort,我重新写了一下,基本没变. 这个例子中定义的map和reduce如下,关键是它对输入输出类型的定义:(java泛型编程) public static ...

  9. C#简单多线程使用(同步和优先权)

    题目: 麦当劳有两个做汉堡的厨师(工号:11,12)和三个销售人员(工号:21,22,23). 厨师生产汉堡,并负责将做好的汉堡放入货架,货架台大小有限,最多放6个汉堡,11和12不能同时往货架台上放 ...

  10. HTML快速入门5——不规则表格、表格背景、边框颜色

    转自:http://blog.csdn.net/ysuncn/article/details/2214153 不规则表格 例子: <table border=1><tr>< ...