1、出现原因:

在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象不满足的。(所以可以在他们之间建立一个适配器的中间类

2、意图:

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由接口不兼容而不能一起工作的那些类可以一起工作。(就是在新环境的 接口 和 原来的 类之间 建立一个适配器,将他们联系起来)

3、两种实现适配器模式的方式

1》对象适配器

推荐使用,因为相对于下面的类继承的方式耦合度更低 

结构图:

1、对象适配器采用对象组合,通过引用一个类(原来的类)与另一个类接口(实现新的环境接口) 在对象适配器中通过组合获得Adaptee对象

2、通过调用Adaptee对象的方法,转换后返回Target结果

2》类的多继承方式

不推荐使用,因为使用继承实现的,对象之间的耦合度 过高

结构图:

1、类适配器通过多继承对一个接口与另一个接口进行匹配。

2、Target定义了Client使用的与特定领域相关的接口,Client通过调用Target实现某一个特定的操作。Adaptee是一个已经存在的类,需要与Target协同工作,这个接口需要适配。Adapter适配器适配Adaptee和Target接口。在类适配器中,通过继承获得Adaptee中的方法。

3、.NET不支持多重继承,因此当Target是一个类,而不是一个接口时无法实现类适配器,这时需要使用对象适配器。(如果,Target是抽象类的话,就不行了,因为 适配器在 c#里面是不支持多继承的),所以Target要是 接口,因为旧的系统已经继承了

4、代码演示

实现需求如下图:

实现代码:

     //新的环境(.Net不支持多继承,所以要使用接口),一个新的环境一个接口(然后 适配器 实现多个接口)
public interface FootballPlayer
{
void Attact();
void Defende();
} public class FQianFeng : FootballPlayer
{
public void Attact()
{
Console.WriteLine("我是足球前锋,我要进攻了");
} public void Defende()
{
Console.WriteLine("我是足球前锋,我要防守了");
}
} public class FZhongFeng : FootballPlayer
{
public void Attact()
{
Console.WriteLine("我是足球中锋,我要进攻了");
} public void Defende()
{
Console.WriteLine("我是足球中锋,我要防守了");
}
} public class FHouWei : FootballPlayer
{
public void Attact()
{
Console.WriteLine("我是足球后卫,我要进攻了");
} public void Defende()
{
Console.WriteLine("我是足球后卫,我要防守了");
}
} //另一个新的环境 接口
public interface BasketBallPlayer
{
void JinGong();
void FangShou();
} public class BQianFeng : BasketBallPlayer
{
public void JinGong()
{
Console.WriteLine("我是篮球前锋,我要进攻了");
} public void FangShou()
{
Console.WriteLine("我是篮球前锋,我要防守了");
}
} //要被适配的 类(Adaptee)
//中国运动员:既会 打篮球,又会 踢足球
public class ChinesePlayer
{
public void 足球进攻()
{
Console.WriteLine("我是中国运动员,我要进行足球进攻了");
}
public void 足球防守()
{
Console.WriteLine("我是中国运动员,我要进行足球防守了");
}
public void 篮球进攻()
{
Console.WriteLine("我是中国的运动员,我要进行篮球进攻了");
}
public void 篮球防守()
{
Console.WriteLine("我是中国的运动员,我要进行篮球防守了");
}
} //下面是一个 Adapter适配器的类
public class Adapter : FootballPlayer, BasketBallPlayer
{
//其实这里举的例子不适当,因为 是 先有 旧的系统(Adaptee被适配对象),才有新的环境。而且是 新的环境要使用旧的系统,才不得已使用适配器模式,,而且两个 新的环境 使用的 同一 旧的系统(也就是使用的同一 对象) ChinesePlayer player = new ChinesePlayer();//这里使用的是 对象组合的方式 实现的 适配器模式
public void Attact()
{
player.足球进攻();
} public void Defende()
{
player.足球防守();
} public void JinGong()
{
player.篮球进攻();
} public void FangShou()
{
player.篮球防守();
}
}

Adapter模式

客户端代码:

             //第一个环境  使用旧的对象
FootballPlayer footPlayer = new FQianFeng();
FootballPlayer footChinesePlayer = new Adapter();
footPlayer.Attact();
footPlayer.Defende();
footChinesePlayer.Attact();
footChinesePlayer.Defende(); //第二个环境 使用旧的对象
BasketBallPlayer basQianFeng = new BQianFeng();
BasketBallPlayer basChinesePlayer = new Adapter();
basQianFeng.JinGong();
basQianFeng.FangShou();
basChinesePlayer.JinGong();
basChinesePlayer.FangShou();

客户端调用代码

其实适配器 起的 作用就是承上启下的作用。承上实现新的环境的接口,然后在新的接口对应方法的地方调用旧的对象的方法实现想要的 旧对象的 功能。

启下:通过旧对象的引用(对象组合的方式)调用对应的功能方法,或者 通过 继承 旧对象(类的多继承),然后调用 旧对象 的功能方法

5、.Net中的适配器模式

1、DataAdapter:数据适配器

2、DataAdpter使应用程序的数据(sql里面的)操作统一到DataSet上,而与具体的数据库类型无关(sql 是早就存在的,要将sql里面的 统一到 Dataset上面,所以就通过 DataAdapter这个中间适配器)

6、实现要点

1、适配器模式重在转换接口,它能够使原本不能在一起工作的两个类一起工作,所以经常用在类库复用,代码迁移等方面,有一种亡羊补牢的味道(但是,不要硬要套这种模式如果在设计一个类库的时候,就已经想到以后会进行扩展,那么就应该提前做好准备。因为这种模式毕竟是一种 弥补措施

2、类适配器和对象适配器可以根据具体实际情况来选用,但一般情况建议使用对象适配器模式(耦合度较低)

7、效果

通过类的继承(类适配器)或者对象的组合(对象适配器)转换已有的接口为目标接口

8、适用性

1、需要使用一个已经存在的类,但接口与设计要求不符

2、希望创建一个可以复用的类,该类可以与其他不相关的类或者是将来不可预见的类协同工作

9、总结

1、Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况” ,在遗留代码复用、类库迁移等方面非常有用。

2、GoF 23 定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神

3、Adapter模式可以实现的非常灵活不必拘泥于Gof23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数(接口里定义一个重载方法:例如:上面那个例子:可以增加一个重载方法void Attack(ChineseRearguard rearguard),然后在适配器里面进行实现,执行 现存类要执行的事务),来达到适配的目的

Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格(切记千万不能面向实现编程),这样才能在后期很方便地适配。

设计模式之Adapter(适配器模式)的更多相关文章

  1. 设计模式(6)--Adapter(适配器模式)--结构型

    1.模式定义: 适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作. 2.模式特点:  Adapter模式使原本因接口不匹配(或者不兼 ...

  2. [C# 设计模式] Adapter - 适配器模式(两种)

    Adapter - 适配器模式 序 现实生活中,我们常用到适配器. 你当前打开我这篇文章的笔记本电脑,电源的另一边不正连着一块适配器吗? 你平时想将三口插座插进二口插座里面,不也需要一个适配器吗? 整 ...

  3. 设计模式06: Adapter 适配器模式(结构型模式)

    Adapter 适配器模式(结构型模式) 适配(转换)的概念无处不在:电源转接头.电源适配器.水管转接头... 动机(Motivation)在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象 ...

  4. 设计模式 结构型 - 适配器模式 Adapter

    Adapter(适配器模式) ---- 加个“适配器”以便于复用 将一个类的接口转换成客户希望的另一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 应用场景 如果 ...

  5. C++设计模式-Adapter适配器模式

    Adapter适配器模式作用:将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 分为类适配器模式和对象适配器模式. 系统的数据和 ...

  6. C++设计模式-Adapter适配器模式(转)

    Adapter适配器模式作用:将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 分为类适配器模式和对象适配器模式. 系统的数据和 ...

  7. 设计模式之Adapter模式

    说起Adapter,STL里的stack和queue都是adapter,底层是deque,隐藏了deque的一些接口,使得其可以达到FIFO是queue,LIFO是stack. The STL sta ...

  8. 设计模式学习之适配器模式(Adapter,结构型模式)(14)

    参考链接:http://www.cnblogs.com/zhili/p/AdapterPattern.html一.定义:将一个类的接口转换成客户希望的另一个接口.Adapter模式使得原本由于接口不兼 ...

  9. 设计模式C++学习笔记之八(Adapter适配器模式)

      适配器模式,使用之处比较特殊,不属于常规设计模式,主要用于不同系统之间的处理.是将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工 ...

  10. IOS设计模式浅析之适配器模式(Adapter)

    引言 在项目开发中,有时候会遇到这样的一种情景:需要使用以前开发的“一些现存的对象”,但是新环境中要求的接口是这些现存对象所不满足的.怎样应对这种迁移的需求?使得可以复用这些对象,以满足新的应用环境, ...

随机推荐

  1. C++记录debug信息的log类

    取自:http://www.viksoe.dk/code/all_mfc.htm,里面有各种MFC常用的类 // LogFile.h: interface for the CLogFile class ...

  2. angular $apply()以及$digest()讲解1

    一些知名的批评和缺陷.他们都涉及到$digest loop(更新周期)中一个很常见的问题:如何在Angular之外更新$scope? 在哪调用 $apply? 更佳的做法是确保你是在$digest l ...

  3. 仿AS语法来写HTML5—第1章,显示一张图片

    最近开始学习html5,因为一直都是研究as,所以还是觉得as顺眼一点,但是html5也不能不学,于是就想出了,可以把html5用as的语法来写出来,做游戏应该来的比较顺手一些,下面开始第一篇 第一篇 ...

  4. 如何:通过对字符串应用 HTML 编码在 Web 应用程序中防止脚本侵入

    大多数脚本利用发生在用户可以将可执行代码(或脚本)插入您的应用程序时. 默认情况下,ASP.NET 提供请求验证.只要窗体发送包含任何 HTML,该验证都会引发错误. 您可以使用下列方法防止脚本利用: ...

  5. C# 获取web.config配置文件内容

    1.web.config提供对客户端应用程序配置文件的访问. 其有两个属性1.ConnectionStrings 获取当前应用程序默认配置的 ConnectionStringsSection 数据. ...

  6. url 编码(percentcode 百分号编码)

    http://www.imkevinyang.com/2009/08/%E8%AF%A6%E8%A7%A3javascript%E4%B8%AD%E7%9A%84url%E7%BC%96%E8%A7% ...

  7. android里面线程睡眠事件使用方法

    SystemClock.sleep(时间); 不用Thread.sleep()的原因:要抛异常,占用资源

  8. scala实现kmeans算法

    算法的概念不做过都解释,google一下一大把.直接贴上代码,有比较详细的注释了. 主程序: import scala.io.Source import scala.util.Random /** * ...

  9. 【easuyi】---easyui中的验证validatebox自定义

    这里比较简单的使用就不再多说,主要说一下自定义的validatebox. 1.验证密码是否相等,这个直接参考给定的列子就行,这里主要学习这种灵活使用的方式和方法. <input id=" ...

  10. WCF全面解析第一章 WCF 简介

    1.WCF中的 "A","B","C" 介绍 我们先看个生活中的例子,某一天,公司的领导让你去送一份合同文件,送文件的过程你可以选择的交通方 ...