适配器模式

偶尔善良 提交于 2019-11-28 00:37:58

摘要

本文以C#示例说明适配器模式的概念和应用场景。

定义

适配器模式(adapter Pattern, 有时又被称为包装样式或者包装(wrapper), 是软件设计模式的一种。在此种模式中,将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类能在一起工作,做法是将自己的接口包裹在一个已存在的类中。维基百科

解读

  • 关键词:适配;
  • 使得原本由于接口不兼容而不能在一起工作的类可以一起工作;
  • 符合开闭原则;
  • 应用场景: 对接类库(接口);项目重构背景下的新老接口对接;
  • 适配器模式解决的是正在服役的项目中存在的问题;
  • 对于正在开发的新项目,如非必要,不要使用适配器模式,条件允许的情况下请重构;
  • 项目中过多的适配器模式的应用,会让系统变得凌乱不堪,难以把握;
  • 在GoF的设计模式中, 适配器模式分为两种类型, 类适配器模式和对象适配器模式。 由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配,而C#,Java等语言都不支持多重继承,因而这里只介绍对象适配器。

现实生活中的场景

  • 翻译工作
  • 笔记本电源
  • 在Linux系统中运行Windows程序
  • ...

思考

下面用代码示例进行说明:

代码示例

ORM包装

假设数据库ORM有一个Get方法专门用于根据传入的SQL语句获取DataTable,而且此方法所在的Class不允许修改,随着数据的增多,开发人员需要一个获取分页数据的方法且能够自定义排序字段,但却不想每次获取数据时都编写分页查询的语句,这个时候,我们可以适配一个中间类来操作

// adaptee - 获取DataTable public class DataRepo {     public DataTable Get(string select_sql, string connectionString)     {         using (SqlConnection con = new SqlConnection(connectionString))         using (var da = new System.Data.SqlClient.SqlDataAdapter(select_sql, con))         {             var dt = new DataTable();              try             {                 con.Open();                 da.Fill(dt);             }             catch (Exception)             {                 throw;             }              return dt;         }     }  }  // adapter - 分页获取数据 public class UserDataRepo {     public DataTable GetPageTable(string select_sql, int pageIndex, int pageSize, string orderfield = "date desc")     {         int row_from = (pageIndex - 1) * pageSize;         var page_sql = $"select * from ({select_sql}) as cc order by cc.{orderfield} \                          offset {row_from} row fetch next {pageSize} rows only";          var dataRepo = new DataRepo();                                 return dataRepo.Get(page_sql);     } }  // client - 分页获取 public class UserOperationCls {     var u_data_repo = new UserDataRepo();     DataTable table = u_data_repo.GetPageTable("select * from Users where isDelete=false \          and createDate > 2015-1-1",          1,          10,          "createDate desc");      // do something else... } 

上面的例子,在 UserOperationCls 和 DataRepo 之间定义了一个适配类UserDataRepo,解决了用户和ORM之间的接口适配问题。可能例子太过简单,也可能不太恰当,就当是我扔了个砖头,没砸到人就权当是引玉了。

延伸阅读:单接口适配器

当不需要全部接口实现的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择的覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况,因此也称为单接口适配器模式。

  /// <summary> /// 日志操作接口 /// </summary> public interface ILog {     bool Insert(string message);     void Clear();     bool Delete(int id); }  /// <summary> /// 实现ILog接口的抽象基类 /// </summary> public abstract class absLog : ILog {     public abstract void Clear();      public abstract bool Delete(int id);      public virtual bool Insert(string message)     {         throw new NotImplementedException();     } }  /// <summary> /// 用户日志 /// </summary> public abstract class absUserLog : absLog {     /// <summary>     /// 删除指定ID的日志     /// </summary>     /// <param name="id"></param>     /// <returns></returns>     public override bool Delete(int id)     {         throw new NotImplementedException();     }      /// <summary>     /// 清空日志,注意加了sealed关键字     /// </summary>     public sealed override void Clear()     {      } }  /// <summary> /// 更完美一点:彻底隐藏Clear方法 /// </summary> public class WebUserLog : absUserLog {     public override bool Delete(int id)     {         return base.Delete(id);     }      public override bool Insert(string message)     {         return base.Insert(message);     } }  

好了,今天的适配器模式就聊到这里,大家继续回去写BUG吧

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!