系列文章

[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. liunx作业一

    一.linux发行版 linux发行版是以linux为内核,包含了系统软件和应用软件.简化系统安装的工具.软件安装升级的集成管理器. 典型的linux发行版包括:linux内核,一些GNU程序库和工具 ...

  2. mycat高可用方案

    1.建议采用标准的mysql主从复制高可用配置并交付给mycat来完成后端mysql节点的主从自动切换. 2.mycat自身的高可用性 由HAproxy+Mycat集群+Mysql主从所组成的高可用性 ...

  3. [django]django+post+ajax+highcharts使用方法

    直接代码展示: view.py文件代码 from django.http import JsonResponse #django ajax部分 def ajax_kchart(request): ti ...

  4. jstl中的sql:query标签获取的结果如何格式化输出

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  5. 微信公共号的PHP开发(基础篇)——玩一下

    最近没事儿开了个微信号,写点东西给家人啊什么的看,你们想看的话就这个嘛: 然后就意外的看到imooc上的微信公众号开发相关了.每天科研的累累的,做点这个不是很累的东西吧. 微信公共号开发 一.基础 1 ...

  6. struts2的拦截器(Interceptor)与过滤器(Filter)

    一.拦截器与过滤器的区别: 1.filter基于回调函数,我们需要实现的filter接口中doFilter方法就是回调函数,而interceptor则基于Java本身的反射机制,这是两者最本质的区别. ...

  7. 2016.11.6 night NOIP模拟赛 考试整理

    题目+数据:链接:http://pan.baidu.com/s/1hssN8GG 密码:bjw8总结: 总分:300分,仅仅拿了120份. 这次所犯的失误:对于2,3题目,我刚刚看就想到了正确思路,急 ...

  8. 【Python数据分析】Python3操作Excel-以豆瓣图书Top250为例

    本文利用Python3爬虫抓取豆瓣图书Top250,并利用xlwt模块将其存储至excel文件,图片下载到相应目录.旨在进行更多的爬虫实践练习以及模块学习. 工具 1.Python 3.5 2.Bea ...

  9. 使用python实现appium的屏幕滑动

    前些日子写一个滑动手机页面的小脚本,看到大家给的内容都是swipe方法,这里对swipe方法做一个小介绍: Swipe(int start x,int start y,int end x,int y, ...

  10. 使用codemodel生成Java代码

    例子 JCodeModel cm = new JCodeModel(); JDefinedClass dc = cm._class("foo.Bar"); dc.annotate( ...