本笔记摘抄自: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. initramfs打包集成rootfs到image镜像及linux rootfs的正常启动

    最近的项目中需要在仿真机haps及VDK上集成rootfs,中间遇到一些问题,在此整理记录以备忘. rootfs里面集成的busybox版本1.29.3 (buildroot环境中自带) kernel ...

  2. Kafka系列1:Kafka概况

    Kafka系列1:Kafka概况 Kafka是当前分布式系统中最流行的消息中间件之一,凭借着其高吞吐量的设计,在日志收集系统和消息系统的应用场景中深得开发者喜爱.本篇就聊聊Kafka相关的一些知识点. ...

  3. 06讲案例篇:系统的CPU使用率很高,但为啥却找不到高CPU的应用

    小结 碰到常规问题无法解释的 CPU 使用率情况时,首先要想到有可能是短时应用导致的问题,比如有可能是下面这两种情况. 第一,应用里直接调用了其他二进制程序,这些程序通常运行时间比较短,通过 top ...

  4. permission denied (publickey)问题的解决和向github添加ssh key

    使用ssh key这种方式进行clone ,pull github上面的项目,使用 git clone或者git pull origin master出现permission denied (publ ...

  5. python学习方法

    python学习体系 python相关书籍若干本 了解python基础数据类型 熟悉各种类型的操作方法 理解函数与类的概念 练习练习再练习 前期以被动学习为主,把每个知识点都认真的学过去,后期慢慢再把 ...

  6. Ubuntu下安装spark

    方法一: jps 查看Java 包 sudo apt-get install openjdk** sudo apt-get install scala 选择安装源然后 sudo wget 下载链接 s ...

  7. Educational Codeforces Round 57

    2018.12.28  22:30 看着CF升高的曲线,摸了摸自己的头发,我以为我变强了,直到这一场Edu搞醒了我.. 从即将进入2018年末开始,开启自闭场集合,以纪念(dian)那些丢掉的头发 留 ...

  8. MVVM框架(二)---生命周期

    一.Vue 生命周期图解: 这张图是官方给出的,大家可能都看过.其中我们重点讲述以下几个钩子函数: beforeCreate  -->   created beforeMount   --> ...

  9. 多用as少用强制类型转换

    在 C# 中存在一个名叫静态类型检查的机制,这个机制可以让编译器帮助我们把类型不服的用法找出来,从而使得应用程序在运行期间加少一些类型检查的操作.但是有时候我们还是需要进行运行期类型检查,比如我们在设 ...

  10. gridFS-Nginx的安装与使用

    在使用gridFs的nginx模块时,先确认好你的机器上已经安装好了mongo 首先通过git将最新的gridfs的nginx模块下载到本地 git clone https://github.com/ ...