系列文章

[Head First设计模式]山西面馆中的设计模式——装饰者模式

[Head First设计模式]山西面馆中的设计模式——观察者模式

[Head First设计模式]山西面馆中的设计模式——建造者模式

[Head First设计模式]饺子馆(冬至)中的设计模式——工厂模式

[Head First设计模式]一个人的平安夜——单例模式

[Head First设计模式]抢票中的设计模式——代理模式

[Head First设计模式]面向对象的3特征5原则

[Head First设计模式]鸭子模型——策略模式

[Head First设计模式]云南米线馆中的设计模式——模版方法模式

[Head First设计模式]餐馆中的设计模式——命令模式

适配器

假设我们已经有一个软件系统,原来使用了一个第三方类库A。现在有一个新的第三方类库B,其功能等各方面都更加强大。我们希望用B来替换A,以改善我们的系统。但是B的接口与A不一样。那怎么办呢?

方案一:重建一个新系统,实现B接口
方案二:找个中间的适配器

适配器模式定义

将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

  • 有时,为复用而设计的工具箱类不能够被复用的原因仅仅是因为它的接口与专业应用领域所需要的接口不匹配(名称不一样,参数不一样等等)
  • 我们可以改变工具箱类使它兼容专业领域中的类的接口,但前提是必须有这个工具箱的源代码,然而 即使我们得到了这些源代码,修改工具箱也是没有什么意义的,因为不应该仅仅为了实现一个应用,工具箱就不得不采用一些与特定领域相关的接口。
  • 我们可以不用上面的方法,而定义一个适配器类,由他来适配工具箱接口和专业应用的接口。我们可以用两种方法做这件事:
  1. 继承专业应用类的接口和工具箱类的实现。这种方法对应Adapter模式的类版本(多继承)
  2. 将工具箱类的实现作为适配器类的组成部分,并且使用工具箱的接口实现适配器类。这种方法对应Adapter模式的对象版本。

适用性:

以下情况使用Adapter模式:

  • 你想使用一个已经存在的类,而它的接口不符合你的需求。
  • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。

类图

适配器模式分为类适配器模式和对象适配器模式。

类适配器模式类图:

基于类的Adapter模式的一般结构:Adaptee类为Adapter的父类,Adaptee类为适配源,适配目标(接口)也是Adapter的父类,基于类的Adapter模式比较适合应用于Adapter想修改Adaptee的部分方法的情况。

对象适配器模式:

基于对象的Adapter模式的一般结构:Adaptee类对象为Adapter所依赖,适配目标(接口)是Adapter的父类。

基于对象的Adapter模式比较适合应用于Adapter想为Adaptee添加新的方法的情况。但在Adaptee类的方法与Adapter类的方法不同名而实现功能的情况下,我们一般也使用基于对象的Adapter模式。

参与者

Target:client使用的与特定领域相关的“接口”。

Client:与符合Target接口的对象协同的专业系统。

Adaptee:一个已经存在的“接口”,它具有Client要求的功能但不符合Client的接口要求。这个接口需要适配。

Adapter:对Adaptee的接口与Target接口进行适配。

协作

Client在Adapter实例上调用一些操作(请求)。接着适配器调用Adaptee的操作实现这个请求。

 效果(类适配器和对象适配器有不同的权衡)

类适配器

用一个具体的Adapter类对Adaptee和Target进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作。

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

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

对象适配器

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

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

代码

简单模拟一下,中国人说汉语,美国人说英语。

 namespace Wolfy.适配器模式
{
/// <summary>
/// 汉语语言接口
/// </summary>
public interface IChinese
{
void Speak();
}
}

IChinese

 namespace Wolfy.适配器模式
{
/// <summary>
/// 英语语言接口
/// </summary>
public interface IEnglish
{
void Speak();
}
}

IEnglish

 namespace Wolfy.适配器模式
{
public class Chinese:IChinese
{
public void Speak()
{
Console.WriteLine("你好");
}
}
}

Chinese

 namespace Wolfy.适配器模式
{
public class American:IEnglish
{
public void Speak()
{
Console.WriteLine("Hello");
}
}
}

American

我们原来有个程序使用的中国人的对象,现在想让它使用美国人对象,但是现在美国人和中国人的语言接口不同,不能直接使用。
写一个美国人的适配器,让他看起来像中国人。

对象适配器模式:

 namespace Wolfy.适配器模式
{
/// <summary>
/// 让美国人适配器类 实现汉语接口
/// </summary>
public class AmericanAdapter:IChinese
{
/// <summary>
/// 美国人适配器包装了一个美国人对象,同时实现了汉语接口。这样就可以像使用中国人对象一样使用美国人对象了。
/// </summary>
IEnglish American;
public AmericanAdapter(IEnglish American)
{
this.American = American;
}
public void Speak()
{
American.Speak();
}
}
}

测试:

  class Program
{
static void Main(string[] args)
{
Chinese me = new Chinese();
American American = new American();
//美国人适配器 让他看起来像中国人对象 这里IChinese
IChinese adapter = new AmericanAdapter(American);
Console.WriteLine("美国人说:");
American.Speak();
Console.WriteLine("中国人说:");
Test(me);
Console.WriteLine("美国人适配器说:");
//在需要中国人对象的地方使用了美国人配器对象,
//美国人适配器对象包装了一个实现英语接口的美国人对象,所以实际使用的是美国人对象。
Test(adapter);
Console.Read();
}
/// <summary>
/// 测试
/// </summary>
/// <param name="chinese">实现汉语接口的中国人对象</param>
static void Test(IChinese chinese)
{
chinese.Speak();
}
}

结果:

类适配器模式:

让上述例子变为类适配器模式,只需修改为:

 namespace Wolfy.适配器模式
{
public class Adapter:American,IChinese
{ }
}

总结

个人感觉,适配器模式,似乎在后期扩展程序的时候用。适配器模式,就像一个中间人,两个不兼容的家伙在一起,屁话没有,通过中间人,比较容易沟通。

参考:

《Head First 设计模式》

[Head First设计模式]身边的设计模式——适配器模式的更多相关文章

  1. [Head First设计模式]生活中学设计模式——迭代器模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  2. [Head First设计模式]生活中学设计模式——组合模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  3. [Head First设计模式]生活中学设计模式——外观模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  4. [Head First设计模式]生活中学设计模式——状态模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  5. 设计模式(五)适配器模式Adapter(结构型)

      设计模式(五)适配器模式Adapter(结构型) 1. 概述: 接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题.程序提供者们修改他们的代码;系统库被修正;各种程序语言以及相 ...

  6. JAVA设计模式——第 8 章 适配器模式【Adapter Pattern】(转)

    好,请安静,后排聊天的同学别吵醒前排睡觉的同学了,大家要相互理解嘛.今天讲适配器模式,这个模式也很简单,你笔记本上的那个拖在外面的黑盒子就是个适配器,一般你在中国能用,在日本也能用,虽然两个国家的的电 ...

  7. Java设计模式(9)适配器模式(Adapter模式)

    适配器模式定义:将两个不兼容的类纠合在一起使用,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份. 为何使用适配器模式 我们经常碰到要将两个没有关系的类组合在一起使用 ...

  8. php设计模式课程---8、适配器模式是什么

    php设计模式课程---8.适配器模式是什么 一.总结 一句话总结: 充电过程中,手机充电器相对于手机和插座之间就是适配器 1.编程中的适配器是怎么回事? 写一个类(适配器),将传入的数据的格式或者内 ...

  9. 【设计模式】Java设计模式 - 适配器模式

    [设计模式]Java设计模式 - 适配器模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一 ...

随机推荐

  1. mybatis 配置返回集合collection时只有一条记录

    查询语句配置如下: <select id="selectCustomerList" resultMap="CustomerDtoMap" paramete ...

  2. C++的友元类和友元函数实例

    #include <math.h> #include<iostream> using namespace std; class Point { public: Point(do ...

  3. maven archetype生成自定义项目原型(模板)

    maven archetype可以将一个项目做成项目原型,之后只需要以此原型来创建项目,那么初始创建的项目便具有原型项目中的一切配置和代码.通俗讲就是一个项目模板. eclipse中那些快速生成mav ...

  4. 帆软报表FineReport中数据连接的JDBC连接池属性问题

    连接池原理 在帆软报表FineReport中,连接池主要由三部分组成:连接池的建立.连接池中连接使用的治理.连接池的关闭.下面就着重讨论这三部分及连接池的配置问题. 1. 连接池原理 连接池技术的核心 ...

  5. C#学习笔记-Windows窗体自定义初始位置

    根据屏幕大小定义初始位置: (这个不是难,但是最近常常忘记,记着方便查看.) //获取当前屏幕的长和宽 int ScreenX = Screen.PrimaryScreen.Bounds.Width; ...

  6. HTTPS简述

    Https是什么? HTTPS是Http Over SSL,简单来说就是HTTP的安全版本,在http之上使用TLS,SSL加密协议. HTTPS实质是在HTTP基础上使用非对称加密相互生成并传递对称 ...

  7. 洛谷P2412 查单词 [trie树 RMQ]

    题目背景 滚粗了的HansBug在收拾旧英语书,然而他发现了什么奇妙的东西. 题目描述 udp2.T3如果遇到相同的字符串,输出后面的 蒟蒻HansBug在一本英语书里面找到了一个单词表,包含N个单词 ...

  8. AC日记——最大数 洛谷 P1198 [JSOI2008]

    题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制:L不超过当前数列的长度. 2. 插入操作 ...

  9. 进程间通信(linux)(转)

    原帖发表在IBM的developerworks网站上,是一个系列的文章,作者郑彦兴,通过讲解和例子演示了Linux中几种IPC的使用方式,我觉得很好,在这里做一个保留,能看完的话Linux IPC的基 ...

  10. CSS基本知识5-CSS对齐

    要对齐的关键,在于理解块,行的概念,块的对齐主要靠自动计算定位,比如margin:auto,及浮动,所以最好的办法是尽量使用行来对齐. 实例: .box { border: 1px solid #80 ...