借用terrylee的原话:
Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
适配器模式再次体现了“面向接口编程,而非面向实现编程”这一精神。
场景:
有一个基于数据库的系统,里面的数据库操作就拿最常用的查询来说,主要是用SqlHelper类里的QueryData(string sql)这个方法来处理的,后来意外发现该方法实现上性能并不是最好(或者不能满足新的需要),而这时正好有一个第三方的DbHelper程序集,写得很成熟性能也不错,但唯一不足的是里面的查询方法签名是SelectData(string sql),怎么办?所有引用SqlHelper的地方全部修改,重头编译么?No,没人会想这样
先看下原来的代码:
using
System;
using System.Data;
namespace Adapter
{
class Program
{
static void Main( string [] args)
{
IDBHelper dbhelper = new SqlHelper();
dbhelper.QueryData( " Select * from TableA " );
Console.ReadKey();
}
}
public interface IDBHelper
{
DataSet QueryData( string sql);
}
public class SqlHelper : IDBHelper
{
public DataSet QueryData( string sql)
{
Console.WriteLine( " QueryData is Called,the sql is :\ " { 0 }\ "" ,sql);
return new DataSet(); // 这里演示起见,就直接返回一个DataSet实例完事 :)
}
}
}
using System.Data;
namespace Adapter
{
class Program
{
static void Main( string [] args)
{
IDBHelper dbhelper = new SqlHelper();
dbhelper.QueryData( " Select * from TableA " );
Console.ReadKey();
}
}
public interface IDBHelper
{
DataSet QueryData( string sql);
}
public class SqlHelper : IDBHelper
{
public DataSet QueryData( string sql)
{
Console.WriteLine( " QueryData is Called,the sql is :\ " { 0 }\ "" ,sql);
return new DataSet(); // 这里演示起见,就直接返回一个DataSet实例完事 :)
}
}
}
如何在尽量不影响原有客户端代码的情况下,用新的DbHelper来取代旧的SqlHelper呢?
假如第三方的DBHelper结构如下:
///
<summary>
/// 第三方的新dbHelper,实际场景中,这个类通常都是封装在程序集中以dll提供,客户端程序无法修改
/// </summary>
public class DbHelper
{
public DataSet SelectData( string sql)
{
Console.WriteLine( " SelectData is Called,the sql is :\ " { 0 }\ "" , sql);
return new DataSet(); // 这里演示起见,就直接返回一个DataSet实例完事 :)
}
}
/// 第三方的新dbHelper,实际场景中,这个类通常都是封装在程序集中以dll提供,客户端程序无法修改
/// </summary>
public class DbHelper
{
public DataSet SelectData( string sql)
{
Console.WriteLine( " SelectData is Called,the sql is :\ " { 0 }\ "" , sql);
return new DataSet(); // 这里演示起见,就直接返回一个DataSet实例完事 :)
}
}
可以新增一个适配器:
///
<summary>
/// 新增的适配器
/// </summary>
public class DBHelperAdapter : IDBHelper
{
private DbHelper _dbHelper;
public DBHelperAdapter(DbHelper dbHelper)
{
this ._dbHelper = dbHelper;
}
public DataSet QueryData( string sql)
{
return _dbHelper.SelectData(sql);
}
}
/// 新增的适配器
/// </summary>
public class DBHelperAdapter : IDBHelper
{
private DbHelper _dbHelper;
public DBHelperAdapter(DbHelper dbHelper)
{
this ._dbHelper = dbHelper;
}
public DataSet QueryData( string sql)
{
return _dbHelper.SelectData(sql);
}
}
这样原有的客户端程序,只需要把
IDBHelper dbhelper = new SqlHelper();
改成:
IDBHelper dbhelper = new DBHelperAdapter(new DbHelper()); 就万事大吉了,当然你可以用配置文件+反射,完全解耦,此处略过
反思:
本例中之所以能轻易将新的类替换旧的类,主要得益于旧的代码仅依赖于抽象(即接口IDBHelper),而非具体的实现(即类SqlHelper),否则也不可能达到最终效果。
OO原则中的"面对接口编码","依赖倒置"的妙处也就在于此。
最后给出类图: