c#设计模式(6)——适配器模式
一、引言
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
接下来要说个例子来演示适配器模式的使用。其中我们一直在使用sqlserver进行增删查改的操作,突然某一天,感觉性能不行了,想替换成redis这种nosql的增删查改的方式,但是redis的帮助类的并没有和sqlserver查询类的接口命名一致,直接替换成本大,这时就需要一个中间层来适配,使得原来sqlserver的数据库操作无缝对接redis数据库操作。
二、介绍
2.1 定义
下面让我们看看适配器的定义,适配器模式——把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法一起工作的两个类能够在一起工作。适配器模式有类的适配器模式和对象的适配器模式两种形式,下面我们先来描述一下类适配器模式。
2.2 类适配器
1. 在说适配器前,先展示一下sqlserver帮助类的实现。首先,先定义统一的增删查改帮助类接口。
/// <summary> /// sql帮助抽象类 /// </summary> public interface IHelper { void Query<T>(); void Add<T>(); void Update<T>(); void Delete<T>(); }
2.接着就是sqlserver帮助类实现IHelper接口.
public class SqlServerHelper : IHelper { public SqlServerHelper() { Console.WriteLine($"{this.GetType().Name} 被构造"); } public void Add<T>() { Console.WriteLine($"{this.GetType().Name}的Add正在添加数据"); } public void Delete<T>() { Console.WriteLine($"{this.GetType().Name}的Delete正在删除数据"); } public void Query<T>() { Console.WriteLine($"{this.GetType().Name}的Query正在查询数据"); } public void Update<T>() { Console.WriteLine($"{this.GetType().Name}的Update正在更新数据"); } }
3.sqlserver一直用得好好的,现在业务升级了,数据库撑不住了,关系型数据库性能堪忧,Redis这种nosql很好,但是有个问题Redis有自定义的帮助类,如果需要升级使用Redis,不能直接替换sqlserver的帮助类的接口方法;因为RedisHelper没有实现IHelper接口;遇到一个想要放一起使用但是却不能一起使用的问题。这时我们的适配器就要登场了,先演示一下类适配器。
/// <summary> /// redis帮助类,由于执行方法与sql的执行方法不一致,需要适配 /// </summary> public class RedisHelper { public RedisHelper() { Console.WriteLine($"{this.GetType().Name} 被构造"); } public void RedisAdd<T>() { Console.WriteLine($"{this.GetType().Name}的RedisAdd正在添加数据"); } public void RedisDelete<T>() { Console.WriteLine($"{this.GetType().Name}的RedisDelete正在删除数据"); } public void RedisQuery<T>() { Console.WriteLine($"{this.GetType().Name}的RedisQuery正在查询数据"); } public void RedisUpdate<T>() { Console.WriteLine($"{this.GetType().Name}的RedisUpdate正在更新数据"); } }
/// <summary> /// 类适配器 /// </summary> public class RedisClassHelper : RedisHelper, IHelper { // 继承实现了让IHelper 和Redis不兼容的事儿---适配器的一种; // 继承: // 1.因为是强继承,具有侵入性 // 2.RedisClassHelper只能为RedisHelper服务; public void Add<T>() { base.RedisAdd<T>(); } public void Delete<T>() { base.RedisDelete<T>(); } public void Query<T>() { base.RedisQuery<T>(); } public void Update<T>() { base.RedisUpdate<T>(); } }
class Program { static void Main(string[] args) { // sqlserver查询 IHelper sqlServerHelper = new SqlServerHelper(); sqlServerHelper.Add<DesignPatternStructure>(); sqlServerHelper.Update<DesignPatternStructure>(); sqlServerHelper.Query<DesignPatternStructure>(); sqlServerHelper.Delete<DesignPatternStructure>(); //现在业务升级了,数据库撑不住了,关系型数据库性能堪忧 //Nosql == Redis很好----Redis的帮助类库--ServiceStack //需要升级使用Redis;----不能直接换了;因为RedisHelper没有实现IHelper接口; //遇到一个想要放一起使用但是却不能一起使用的问题; //这时就需要一个适配器了 //类适配器 //继承实现了让IHelper 和Redis不兼容的事儿---适配器的一种; //继承: //1.因为是强继承,具有侵入性 //2.RedisClassHelper只能为Redishelper服务; IHelper redisClassHelper = new RedisClassHelper(); redisClassHelper.Add<DesignPatternStructure>(); redisClassHelper.Update<DesignPatternStructure>(); redisClassHelper.Query<DesignPatternStructure>(); redisClassHelper.Delete<DesignPatternStructure>(); } }
2.2 对象适配器
4. 以上就是类适配器的实现,但是大家都发现了一个问题,就是虽然解决RedisHelper和IHelper不兼容的问题,但是因为是强继承,具有侵入性RedisClassHelper只能为Redishelper服务,而且想要匹配一 个类以及所有它的子类时,类的适配器模式就不能胜任了;为了解决这个问题,多了一种对象适配器,使得适配类可以兼容多个不同的对象。
为了证明这一点,我写一个继承了RedisHelper类的子类
public class RedisChildHelper: RedisHelper { }
然后我写个对象适配器
/// <summary> /// 对象适配器 /// </summary> public class RedisObjHelper : IHelper { private RedisHelper _redisHelper = null; /// <summary> /// 构造函数方式注入 /// 在实例化的时候,必须传入----就可以保障需要适配的对象不会为null /// </summary> /// <param name="redisHelper"></param> public RedisObjHelper(RedisHelper redisHelper) { _redisHelper = redisHelper; } public void SetRedisHelper(RedisHelper redisHelper) { _redisHelper = redisHelper; } public void Add<T>() { _redisHelper.RedisAdd<T>(); } public void Delete<T>() { _redisHelper.RedisDelete<T>(); } public void Query<T>() { _redisHelper.RedisQuery<T>(); } public void Update<T>() { _redisHelper.RedisUpdate<T>(); } }
5.演示一下对象适配器的实现
class Program { static void Main(string[] args) { //对象适配器 //为什么说组合优于继承呢? //1.没有侵入性 //2.可以为多个类型服务 //3.相对来说要更加灵活一点 //IHelper redisObjHelper = new RedisObjHelper(new RedisHelper()); IHelper redisChildHelper = new RedisObjHelper(new RedisChildHelper()); redisObjHelper.Add<DesignPatternStructure>(); redisObjHelper.Update<DesignPatternStructure>(); redisObjHelper.Query<DesignPatternStructure>(); redisObjHelper.Delete<DesignPatternStructure>(); } }
三、适配器模式的优缺点
类适配器模式:
1.优点:
可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
可以重新定义(被适配的类)的部分行为.
仅仅引入一个对象,并不需要额外的字段来引用RedisHelper实例(这个即是优点也是缺点)
2.缺点:
采用了 “多继承”的实现方式,带来了不良的高耦合。
对象适配器模式:
1.优点:可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”,采用 “对象组合”的方式,更符合松耦合,以兼容多个不同的对象