本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7640873.html,记录一下学习过程以备后续查用。

一、引言

从今天开始我们开始讲结构型设计模式,结构型设计模式有如下几种:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

    创建型设计模式解决的是对象创建的问题,而结构型设计模式解决的是类和对象组合关系的问题。

今天我们开始讲结构型设计模式里面的第一个设计模式:适配器模式。适配器模式其实很简单,在现实生活中有很多这样的实例实例:比如,手机充电器的

接头是二插的,假如只有三插的插座,就必须通过三插转二插的转换器才可以正常充电;笔记本电脑的工作电压和家庭照明的电压是不一致的,需要通过变压

器(俗称火牛)才能让笔记本电脑正常工作。适配器的例子数不胜数,只需记住一点:适配就是转换,让不能在一起工作的两样东西通过转换可以正常工作。

二、适配器模式介绍

适配器模式:英文名称--Adapter Pattern;分类--结构型。

2.1、动机(Motivate)

在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新的环境要求的接口是这些现存对象所不能满足的。如何应

对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?

2.2、意图(Intent)

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。--《设计模式》Gof

2.3、结构图(Structure)

适配器有两种结构:

1)对象适配器(更常用)

对象适配器使用的是对象组合的方案,它的Adapter和Adaptee的关系是组合关系。

OO中优先使用组合模式,组合模式不适用时再考虑继承,因为组合模式更加松耦合。而继承是紧耦合的,父类的任何改动都要导致子类的改动。

2)类适配器

2.4、模式的组成

从上两图可以看出,在适配器模式的结构图有以下角色:

1)目标角色(Target):定义Client使用的与特定领域相关的接口。

2)客户角色(Client):与符合Target接口的对象协同。

3)被适配角色(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。

4)适配器角色(Adapter) :适配器模式的核心,它将对被适配Adaptee角色已有的接口转换为目标角色Target匹配的接口并进行适配。

2.5 、适配器模式的具体实现

2.5.1对象适配器模式的实现

    class Program
{
/// <summary>
/// 目标角色(Target)--两孔插座,这里可以写成抽象类或者接口。
/// </summary>
public class TwoHoleTarget
{
//客户端需要的方法
public virtual void Request()
{
Console.WriteLine("我需要两孔的插座。");
}
} /// <summary>
/// 源角色(Adaptee)--三孔插座,需要适配的类。
/// </summary>
public class ThreeHoleAdaptee
{
public void SpecificRequest()
{
Console.WriteLine("增加三孔转两孔的插座,两孔充电器也可以使用了。");
}
} /// <summary>
/// 适配器类
/// </summary>
public class ThreeToTwoAdapter : TwoHoleTarget
{
//创建三孔插座的实例
private ThreeHoleAdaptee threeHoleAdaptee = new ThreeHoleAdaptee(); /// <summary>
/// 实现两孔插座接口方法
/// </summary>
public override void Request()
{
//具体的转换工作
threeHoleAdaptee.SpecificRequest();
}
} static void Main(string[] args)
{
#region 适配器模式之对象适配器
TwoHoleTarget twoHole = new ThreeToTwoAdapter();
twoHole.Request();
Console.ReadLine();
#endregion
}
}

运行结果如下:

    2.5.2类适配器模式实现

    class Program
{
/// <summary>
/// 目标角色(Target)--两孔插座,这里只能是接口,也是类适配器的限制。
/// </summary>
public interface ITarget
{
void Request();
} /// <summary>
/// 源角色(Adaptee)--三孔插座,需要适配的类。
/// </summary>
public abstract class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("增加三孔转两孔的插座,两孔充电器也可以使用了。");
}
} /// <summary>
/// 适配器类,接口要放在类的后面,在此无法适配更多的对象,这是类适配器的不足。
/// </summary>
public class Adapter : Adaptee, ITarget
{
/// <summary>
/// 实现两孔插座接口方法
/// </summary>
public void Request()
{
//具体的转换工作
SpecificRequest();
}
} static void Main(string[] args)
{
#region 适配器模式之类适配器
ITarget twoHole = new Adapter();
twoHole.Request();
Console.ReadLine();
#endregion
}
}

运行结果如下:

    三、适配器模式的实现要点

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

    2)GoF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。类适配器采用“多继承”的实现方式,在C#语言中,如果被适配角色是类,Target的

实现只能是接口,因为C#语言只支持接口的多继承。在C#语言中类适配器也很难支持适配多个对象的情况,同时也会带来了不良的高耦合和违反类的单一职

责的原则,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神,对适配的对象也没限制,可以一个也可以多个,但是,这也使得重

定义Adaptee的行为比较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。Adapter模式可以实现的非常灵活,不必

拘泥于GoF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。

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

下面详细总结下适配器两种形式的优缺点:

    3.1、对象适配器模式

优点:

    1)可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”。

    2)采用 “对象组合”的方式,更符合松耦合。

缺点:

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

3.2、类适配器模式

优点:

    1)可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”。

    2)可以重新定义Adaptee(被适配的类)的部分行为,因为在类适配器模式中,Adapter是Adaptee的子类。

    3)仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例(这个即是优点也是缺点)。

缺点:

    1)用一个具体的Adapter类对Adaptee和Target进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模

式中没有引入Adaptee的实例,光调用SpecificRequest方法并不能去调用它对应子类的SpecificRequest方法。

    2)采用了 “多继承”的实现方式,带来了不良的高耦合。

3.3、适配器模式的使用场景

    1)系统需要复用现有类,而该类的接口不符合系统的需求。

    2)想要建立一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

    3)对于对象适配器模式,在设计里需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。

    四、.NET中适配器模式的实现

说到适配器模式在.Net中的实现就很多了,比如:System.IO里面的很多类都有适配器的影子,当我们操作文件的时候,其实里面调用了COM的接口实现。

以下两点也是适配器使用的案例:

    4.1、.NET中复用COM对象

COM对象不符合.NET对象的接口,使用tlbimp.exe来创建一个Runtime Callable Wrapper(RCW)以使其符合.NET对象的接口,COM Interop就好像是

COM和.NET之间的一座桥梁。

4.2、.NET数据访问类(Adapter变体)

各种数据库并没有提供DataSet接口,使用DbDataAdapter可以将任何数据库访问/存取适配到一个DataSet对象上,DbDataAdapter在数据库和DataSet之间

做了很好的适配。当然还有SqlDataAdapter类型,针对微软SqlServer类型的数据库在和DataSet之间进行适配。

    五、总结

有一句话还是要说的,虽然以前说过。每种设计模式都有自己的适用场景,它是为了解决一类问题,没有所谓的缺点,没有一种设计模式可以解决所有情况

的。我们使用设计模式的态度是通过不断地重构来使用模式,不要一上来就使用设计模式,为了模式而模式。如果软件没有需求的变化,我们不使用模式都没

有问题。遇到问题,我们就按着常规来写,有了需求变化,然后我们去抽象,了解使用的场景,然后再选择合适的设计模式。

C#设计模式学习笔记:(6)适配器模式的更多相关文章

  1. 设计模式学习笔记——Adapter 适配器模式

    适配器设计模式的适应场景: 一般情况是上端固定,下端固定,下端功能不满足或跟上端不协调,使用适配器重新包一层(继承适配器接口,以满足上端需求,继承下层类,以调用方法),使下端代码能满足上端需求(欺骗, ...

  2. 7 种 Javascript 常用设计模式学习笔记

    7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...

  3. C#设计模式学习笔记-单例模式随笔

    最近学习 设计模式,从单例模式入手 啥是单例模式: 要实现一个单例类的话,首先,肯定是不能让用户自行生产的,那就是说明不能让用户new,所以,就必须把构造函数设置成为私有的 因为静态变量的生命周期跟整 ...

  4. 设计模式学习笔记--备忘录(Mamento)模式

    写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...

  5. C#设计模式学习笔记-单例模式(转)

    C#设计模式学习笔记-单例模式 http://www.cnblogs.com/xun126/archive/2011/03/09/1970807.html 最近在学设计模式,学到创建型模式的时候,碰到 ...

  6. Java设计模式学习笔记(二) 简单工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...

  7. Java设计模式学习笔记(三) 工厂方法模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...

  8. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

  9. Java设计模式学习笔记(五) 单例模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 使用单例模式的原因 以Windows任务管理器为例,在Windows系统中,任务管理器是唯 ...

  10. C#设计模式学习笔记:(3)抽象工厂模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...

随机推荐

  1. Hyper-V虚拟机Redhat添加网卡找不到网卡配置文件解决方法

    环境:Hyper-V虚拟机上面安安装Redhat6.7 问题:系统安装时,只有一块网卡,现根据需要,增加一块网卡,DHCP获取IP地址,在Hyper-V设置中增加网卡后,存在连个问题:1./etc/s ...

  2. 整合spring-data-redis以及redisTemplate的使用

    一.导入依赖配置 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis< ...

  3. 什么是ip地址、子网掩码、网关和DNS?

    什么是ip地址? IP是32位二进制数据,通常以十进制表示,并以“.”分隔.IP地址是一种逻辑地地址,用来标识网络中一个个主机,IP有唯一性,即每台机器的IP在全世界是唯一的. IP地址=网络地址+主 ...

  4. MVVM的理解和Vue的生命周期

    一.对于MVVM的理解? MVVM 是 Model-View-ViewModel 的缩写.Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑.View 代表UI 组件,它负责将数 ...

  5. js的new操作符深度解析

    引言 我们都知道new操作符在js中一般是用来创建一个构造函数的实例,它在创建实例具体做了什么,MDN文档是这么说的: 我一开始看到,完全没有任何的头绪和理解,到底什么意思,后面通过上网查阅了大量的资 ...

  6. axure如何实现提示框3s后自动消失

    本示例基于axure8 实现 1.先做两个元件,一个按钮,一个提示框 2.将弹框“发布成功提示”设置为,页面载入时隐藏,这样预览页面时,该弹框是隐藏状态 3.给按钮添加交互样式,如下: 4.预览,点击 ...

  7. kvm命令

    查询:virsh -c     qemu:///system list    查看当前的虚拟系统 brctl show     列出当前所有的网桥接口virsh list   列出运行的虚拟机virs ...

  8. ffmpeg常用数据结构

    from :http://my.oschina.net/u/555701/blog/56748 AVCodecContext 这是一个描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,如 ...

  9. excel 2010 如何设置日期选择器

    excel 中想输入很多的日期.如果每个日期都直接手动输入太过于繁琐,而且容易出错.想制作一个日期选择器,直接鼠标点选就可以了. 效果如下: 具体实现参考 http://wenku.baidu.com ...

  10. sqlserver check running process 1

    check process script 1, check which is current running: use master SELECTspid,ER.percent_complete,CA ...