23种设计模式之适配器模式(Adapter Pattern)
适配
即在不改变原有实现的基础上,将原先不兼容的接口转换为兼容的接口。
例如:二转换为三箱插头,将高电压转换为低电压等。
动机(Motivate):
在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
那么如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?这就是本文要说的Adapter 模式。
意图(Intent):
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
-------《设计模式》GOF
抽象的 UML 类图
4 种角色:Adaptee(被适配),Adapter(适配者),Client(使用场景),Target(目标对象)。
Adaptee(被适配):不是 -er 结尾的哦,之前的 Portal(入口)类作为被适配者。
Adapter(适配者):作为 Adaptee 和 Target 的媒介,进行调节。
Client(使用场景):一个调用的入口,以 Main() 作为入口函数。
Target(目标对象):调节(适配)后的输出,之前的 IOutput 接口和 Export 类都是作为 Target 对象。
图:类适配器(使用继承)
图:对象适配器(使用委托)
1 /// <summary> 2 /// 数据访问接口 3 /// </summary> 4 public interface IHelper 5 { 6 void Add<T>(); 7 void Delete<T>(); 8 void Update<T>(); 9 void Query<T>(); 10 }
1 public class MysqlHelper : IHelper 2 { 3 public void Add<T>() 4 { 5 Console.WriteLine("This is {0} Add", this.GetType().Name); 6 } 7 public void Delete<T>() 8 { 9 Console.WriteLine("This is {0} Delete", this.GetType().Name); 10 } 11 public void Update<T>() 12 { 13 Console.WriteLine("This is {0} Update", this.GetType().Name); 14 } 15 public void Query<T>() 16 { 17 Console.WriteLine("This is {0} Query", this.GetType().Name); 18 } 19 }
1 public class OracleHelper : IHelper 2 { 3 public void Add<T>() 4 { 5 Console.WriteLine("This is {0} Add", this.GetType().Name); 6 } 7 public void Delete<T>() 8 { 9 Console.WriteLine("This is {0} Delete", this.GetType().Name); 10 } 11 public void Update<T>() 12 { 13 Console.WriteLine("This is {0} Update", this.GetType().Name); 14 } 15 public void Query<T>() 16 { 17 Console.WriteLine("This is {0} Query", this.GetType().Name); 18 } 19 }
1 public class SqlserverHelper : IHelper 2 { 3 public void Add<T>() 4 { 5 Console.WriteLine("This is {0} Add", this.GetType().Name); 6 } 7 public void Delete<T>() 8 { 9 Console.WriteLine("This is {0} Delete", this.GetType().Name); 10 } 11 public void Update<T>() 12 { 13 Console.WriteLine("This is {0} Update", this.GetType().Name); 14 } 15 public void Query<T>() 16 { 17 Console.WriteLine("This is {0} Query", this.GetType().Name); 18 } 19 }
上面的代码mysql Oracle SQLserver都实现了IHelper接口这里如果想要对接Redis就需要使用适配器
1 /// <summary> 2 /// 第三方提供的 openstack servicestack 3 /// 不能修改 4 /// </summary> 5 public class RedisHelper 6 { 7 public void AddRedis<T>() 8 { 9 Console.WriteLine("This is {0} Add", this.GetType().Name); 10 } 11 public void DeleteRedis<T>() 12 { 13 Console.WriteLine("This is {0} Delete", this.GetType().Name); 14 } 15 public void UpdateRedis<T>() 16 { 17 Console.WriteLine("This is {0} Update", this.GetType().Name); 18 } 19 public void QueryRedis<T>() 20 { 21 Console.WriteLine("This is {0} Query", this.GetType().Name); 22 } 23 }
1 /// <summary> 2 /// 通过继承 类适配器模式 3 /// </summary> 4 public class RedisHelperClass : RedisHelper, IHelper 5 { 6 public void Add<T>() 7 { 8 base.AddRedis<T>(); 9 } 10 11 public void Delete<T>() 12 { 13 base.DeleteRedis<T>(); 14 } 15 16 public void Update<T>() 17 { 18 base.UpdateRedis<T>(); 19 } 20 21 public void Query<T>() 22 { 23 base.QueryRedis<T>(); 24 } 25 }
另外还可以使用适配器执行方法前后做一些拓展
1 public class CacheHelper 2 { 3 public void AddCache<T>() 4 { 5 Console.WriteLine("This is {0} Add", this.GetType().Name); 6 } 7 public void DeleteCache<T>() 8 { 9 Console.WriteLine("This is {0} Delete", this.GetType().Name); 10 } 11 public void UpdateCache<T>() 12 { 13 Console.WriteLine("This is {0} Update", this.GetType().Name); 14 } 15 public void QueryCache<T>() 16 { 17 Console.WriteLine("This is {0} Query", this.GetType().Name); 18 } 19 }
1 /// <summary> 2 /// 通过组合 对象适配器模式 3 /// 4 /// 组合优于继承 5 /// </summary> 6 public class RedisHelperObject : IHelper 7 { 8 //private RedisHelper _RedisHelper = new RedisHelper(); 9 private RedisHelper _RedisHelper = null; 10 11 private CacheHelper _CacheHelper = new CacheHelper(); 12 public RedisHelperObject(RedisHelper redisHelper)//可能是一个抽象接口,注入进来 13 { 14 this._RedisHelper = redisHelper; 15 } 16 17 public RedisHelperObject() 18 { 19 this._RedisHelper = new RedisHelper(); 20 } 21 22 public void Add<T>() 23 { 24 this._CacheHelper.AddCache<T>(); 25 this._RedisHelper.AddRedis<T>(); 26 } 27 28 public void Delete<T>() 29 { 30 this._RedisHelper.DeleteRedis<T>(); 31 } 32 33 public void Update<T>() 34 { 35 this._RedisHelper.UpdateRedis<T>(); 36 } 37 38 public void Query<T>() 39 { 40 this._RedisHelper.QueryRedis<T>(); 41 } 42 }
前端调用
1 Console.WriteLine("*****************************"); 2 { 3 IHelper helper = new SqlserverHelper(); 4 helper.Add<Program>(); 5 helper.Delete<Program>(); 6 helper.Update<Program>(); 7 helper.Query<Program>(); 8 } 9 Console.WriteLine("*****************************"); 10 { 11 IHelper helper = new MysqlHelper(); 12 helper.Add<Program>(); 13 helper.Delete<Program>(); 14 helper.Update<Program>(); 15 helper.Query<Program>(); 16 } 17 Console.WriteLine("*****************************"); 18 { 19 IHelper helper = new OracleHelper(); 20 helper.Add<Program>(); 21 helper.Delete<Program>(); 22 helper.Update<Program>(); 23 helper.Query<Program>(); 24 } 25 Console.WriteLine("*****************************"); 26 { 27 IHelper helper = new RedisHelperClass(); //new RedisHelper(); 28 helper.Add<Program>(); 29 helper.Delete<Program>(); 30 helper.Update<Program>(); 31 helper.Query<Program>(); 32 } 33 { 34 //侵入性特别强 35 RedisHelperClass helper = new RedisHelperClass(); 36 helper.Add<Program>(); 37 //helper 38 } 39 Console.WriteLine("*****************************"); 40 { 41 IHelper helper = new RedisHelperObject(); //new RedisHelper(); 42 helper.Add<Program>(); 43 helper.Delete<Program>(); 44 helper.Update<Program>(); 45 helper.Query<Program>(); 46 }
Adapter模式的几个要点:
Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
GOF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,带来不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。
Adapter模式可以实现的非常灵活,不必拘泥于GOF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象“作为新的接口方法参数,来达到适配的目的。
Adapter模式本身要求我们尽可能地使用”面向接口的编程"风格,这样才能在后期很方便的适配。
.NET框架中的Adapter应用:
(1)在.Net中复用com对象:
Com 对象不符合.net对象的接口
使用tlbimp.exe来创建一个Runtime Callable Wrapper(RCW)以使其符合.net对象的接口。
(2).NET数据访问类(Adapter变体):
各种数据库并没有提供DataSet接口
使用DBDataAdapter可以将任何各数据库访问/存取适配到一个DataSet对象上。
(3)集合类中对现有对象的排序(Adapter变体);
现有对象未实现IComparable接口
实现一个排序适配器(继承IComparer接口),然后在其Compare方法中对两个对象进行比较。
本文参考文档:https://www.cnblogs.com/abcdwxc/archive/2007/09/04/881674.html
作者:德乌姆列特
本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。
博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。
博主是利用读书、参考、引用、抄袭、复制和粘贴等多种方式打造成自己的文章,请原谅博主成为一个无耻的文档搬运工!