摘要
本文以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吧

来源:http://www.cnblogs.com/zanpen2000/p/7607986.html