本笔记摘抄自:http://www.javashuo.com/article/p-ktjdqwpc-gh.html,记录一下学习过程以备后续查用。html
1、引言数据库
从今天开始咱们开始讲结构型设计模式,结构型设计模式有以下几种:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。编程
创建型设计模式解决的是对象建立的问题,而结构型设计模式解决的是类和对象组合关系的问题。设计模式
今天咱们开始讲结构型设计模式里面的第一个设计模式:适配器模式。适配器模式其实很简单,在现实生活中有不少这样的实例实例:好比,手机充电器的app
接头是二插的,假如只有三插的插座,就必须经过三插转二插的转换器才能够正常充电;笔记本电脑的工做电压和家庭照明的电压是不一致的,须要经过变压ide
器(俗称火牛)才能让笔记本电脑正常工做。适配器的例子数不胜数,只需记住一点:适配就是转换,让不能在一块儿工做的两样东西经过转换能够正常工做。学习
2、适配器模式介绍spa
适配器模式:英文名称--Adapter Pattern;分类--结构型。设计
2.一、动机(Motivate)代理
在软件系统中,因为应用环境的变化,经常须要将“一些现存的对象”放在新的环境中应用,可是新的环境要求的接口是这些现存对象所不能知足的。如何应
对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能知足新的应用环境所要求的接口?
2.二、意图(Intent)
将一个类的接口转换成客户但愿的另外一个接口。Adapter模式使得本来因为接口不兼容而不能一块儿工做的那些类能够一块儿工做。--《设计模式》Gof
2.三、结构图(Structure)
适配器有两种结构:
1)对象适配器(更经常使用)
对象适配器使用的是对象组合的方案,它的Adapter和Adaptee的关系是组合关系。
OO中优先使用组合模式,组合模式不适用时再考虑继承,由于组合模式更加松耦合。而继承是紧耦合的,父类的任何改动都要致使子类的改动。
2)类适配器
2.四、模式的组成
从上两图能够看出,在适配器模式的结构图有如下角色:
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 } }
运行结果以下:
3、适配器模式的实现要点
1)Adapter模式主要应用于“但愿复用一些现存的类,可是接口又与复用环境要求不一致的状况”,在遗留代码复用、类库迁移等方面很是有用。
2)GoF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。类适配器采用“多继承”的实现方式,在C#语言中,若是被适配角色是类,Target的
实现只能是接口,由于C#语言只支持接口的多继承。在C#语言中类适配器也很难支持适配多个对象的状况,同时也会带来了不良的高耦合和违反类的单一职
责的原则,因此通常不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神,对适配的对象也没限制,能够一个也能够多个,可是,这也使得重
定义Adaptee的行为比较困难,这就须要生成Adaptee的子类而且使得Adapter引用这个子类而不是引用Adaptee自己。Adapter模式能够实现的很是灵活,没必要
拘泥于GoF23中定义的两种结构。例如,彻底能够将Adapter模式中的“现存对象”做为新的接口方法参数,来达到适配的目的。
3)Adapter模式自己要求咱们尽量地使用“面向接口的编程”风格,这样才能在后期很方便地适配。
下面详细总结下适配器两种形式的优缺点:
3.一、对象适配器模式
优势:
1)能够在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”。
2)采用 “对象组合”的方式,更符合松耦合。
缺点:
1)使得重定义Adaptee的行为较困难,这就须要生成Adaptee的子类而且使得Adapter引用这个子类而不是引用Adaptee自己。
3.二、类适配器模式
优势:
1)能够在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”。
2)能够从新定义Adaptee(被适配的类)的部分行为,由于在类适配器模式中,Adapter是Adaptee的子类。
3)仅仅引入一个对象,并不须要额外的字段来引用Adaptee实例(这个便是优势也是缺点)。
缺点:
1)用一个具体的Adapter类对Adaptee和Target进行匹配,当若是想要匹配一个类以及全部它的子类时,类的适配器模式就不能胜任了。由于类的适配器模
式中没有引入Adaptee的实例,光调用SpecificRequest方法并不能去调用它对应子类的SpecificRequest方法。
2)采用了 “多继承”的实现方式,带来了不良的高耦合。
3.三、适配器模式的使用场景
1)系统须要复用现有类,而该类的接口不符合系统的需求。
2)想要创建一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在未来引进的类一块儿工做。
3)对于对象适配器模式,在设计里须要改变多个已有子类的接口,若是使用类的适配器模式,就要针对每个子类作一个适配器,而这不太实际。
4、.NET中适配器模式的实现
说到适配器模式在.Net中的实现就不少了,好比:System.IO里面的不少类都有适配器的影子,当咱们操做文件的时候,其实里面调用了COM的接口实现。
如下两点也是适配器使用的案例:
4.一、.NET中复用COM对象
COM对象不符合.NET对象的接口,使用tlbimp.exe来建立一个Runtime Callable Wrapper(RCW)以使其符合.NET对象的接口,COM Interop就好像是
COM和.NET之间的一座桥梁。
4.二、.NET数据访问类(Adapter变体)
各类数据库并无提供DataSet接口,使用DbDataAdapter能够将任何数据库访问/存取适配到一个DataSet对象上,DbDataAdapter在数据库和DataSet之间
作了很好的适配。固然还有SqlDataAdapter类型,针对微软SQL Server类型的数据库在和DataSet之间进行适配。
5、总结
有一句话仍是要说的,虽然之前说过。每种设计模式都有本身的适用场景,它是为了解决一类问题,没有所谓的缺点,没有一种设计模式能够解决全部状况
的。咱们使用设计模式的态度是经过不断地重构来使用模式,不要一上来就使用设计模式,为了模式而模式。若是软件没有需求的变化,咱们不使用模式都没
有问题。遇到问题,咱们就按着常规来写,有了需求变化,而后咱们去抽象,了解使用的场景,而后再选择合适的设计模式。