1、概要
适配:即在不改变原有实现的基础上,将原先不适合的接口转换成适合的接口.
what is Apater?适配,这个概念在生活中无处不在,比如你的iphone 4手机充电器坏了,这是时候只有一个iphone 8的充电器,两个充电器的头并不匹配,这个时候,你就需要一个充电器适配器.这个适配器将8的充电器转换成能支持4充电的充电器接口.这个例子在不改变8的充电器原有功能的情况下,将8不适合4的接口通过适配器转变成合适的接口.等等例子还有很多.
2、动机
在软件开发的过程中,常常需要将一些"现存的对象"放到新的环境中去,但是新的环境接口,这些对象并不满足.如何解决这种"迁移的变化",就是适配器模式要解决的问题.
3、意图
将已经稳定的一个类的接口转换成客户需要的接口,Apater模式使用原本由于接口不兼容的而不能一起工作的接口能一起工作.
4、代码实例-对象适配器
现在客户系统在实现一个功能的时候只需要ArrayList的部分功能,需要的功能通过ICustomerReqiured接口定义.这个时候.用适配器模式能很好的解决这个问题!
/// <summary> /// 客户要求的接口 /// </summary> public interface ICustomerRequired { void Push(object item); object Pop(); object Peek(); } /// <summary> /// 对象适配器 /// 对象适配器,Apater创建了一个ArrayList的包装器,缩小了ArrayList的接口范围,如果有特殊的业务需要使用ArrayList的部分方法,可以使用该模式 /// </summary> public class Apater : ICustomerRequired { private ArrayList _apatee;//需要被适配的对象,也是存在的稳定的对象 public Apater(ArrayList arrayList) { _apatee = arrayList; } /// <summary> /// 返回最后一项 /// </summary> /// <returns></returns> public object Peek() { if (_apatee.Count >= 1) return _apatee[_apatee.Count - 1]; return null; } /// <summary> /// 移除最后一项,返回最后一项 /// </summary> /// <returns></returns> public object Pop() { _apatee.RemoveAt(_apatee.Count - 1); return Peek(); } /// <summary> /// 添加一项 /// </summary> /// <param name="item"></param> public void Push(object item) { _apatee.Add(item); } }
当然,失配器远比上面代码所展示的功能要强,不仅仅支持缩小限制ArrayList的功能,也能扩展、修改组合ArrayList的功能.
5、代码实例-类适配器
/// <summary> /// 类适配器 /// 类适配器,Apater继承了ArrayList,拥有了ArrayList类所有方法的同时,有实现客户要求的接口,但是违反了类职责单一的oop原则 /// </summary> public class Apater :ArrayList,ICustomerRequired { public Apater(ICollection collection) : base(collection) { } /// <summary> /// 返回最后一项 /// </summary> /// <returns></returns> public object Peek() { if (Count >= 1) return this[this.Count - 1]; return null; } /// <summary> /// 移除最后一项,返回最后一项 /// </summary> /// <returns></returns> public object Pop() { this.RemoveAt(this.Count - 1); return Peek(); } /// <summary> /// 添加一项 /// </summary> /// <param name="item"></param> public void Push(object item) { this.Add(item); } }
分析上面的代码发现,虽然Apater很好的完成了需求,但是存在以下两个缺点:
1、违背了OOP原则一类的单一职责,Apater即有ArrayList的职责,又包含了ICustomerRequired接口的职责.这种方式的适配器不建议使用,更建议使用对象适配器!
2、类只能单继承,当适配器需要适配多个类时,类适配器就无法完成这个任务.
3、类之间的强耦合关系(在OOP中,继承会自带耦合效果),当被适配的类发生改变时,当前适配器会被强加这种改变.
所以,使用类适配器需要慎重.相比类适配器更推荐第一种对象适配器.
6、关于适配器模式的设计建议
客户端调用,优先使用客户要求的接口类,如下代码:
/// <summary> /// 第三方调用系统 /// </summary> public class ThirdSystem { /// <summary> /// 建议这种调用方式,更符合OOP的思想 /// </summary> /// <param name="customerRequired"></param> public void Process(ICustomerRequired customerRequired) { } /// <summary> /// 不建议这种方法 /// </summary> /// <param name="apater"></param> public void ProcssError(Apater apater) { } }
上面的代码更加符合面向接口的编程思想.
7、.Net Framework中使用适配器模式的案例
public class User { public string Name { get; set; } public int Age { get; set; } } /// <summary> /// IComparer<User> 为客户程序要求适配器实现的接口,必须实现该接口,完成对两个User实例的年龄比较 /// </summary> public class UserCompareApater : IComparer<User> { /// <summary> /// User类作为被适配对象 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public int Compare(User x, User y) { if (x.Age > y.Age) return 1; else if (x.Age < y.Age) return -1; return 0; }
通过适配器,继承了FCL中提供的两个类型比较的接口,实现了对两个User实例的排序。
其它的例子还有很多.如.Net数据库访问类(Apater变体),主流的数据库都没有提供DataSet接口,但是使用DbDataApater可以访问各种数据库,并且将读取过来的数据填充到DataSet实例中.