抽象地说,如果客户需要使用某个类的服务,而这项服务是这个类用一个不同的接口提供的,那么就可以使用适配器模式(Adapter)为客户提供一个期望的接口。
具体地说,现有的类已经提供了客户所需要的服务,但是方法的名称不相同,通过使用适配器模式(Adapter)为客户提供一个类,这个类包含客户需要的完成相同服务且方法名称相同的方法。
这里分两种情况:
(1) 客户用接口定义了它所需要的服务,存在一个类提供了客户所需要的服务。这时,适配器模式(Adapter)是:创建一个新类,它继承提供服务的类,实现了客户的接口。它简称类适配器模式。代码如下:
using System.Collections.Generic;
using System.Text;
/**/ /// <!--
/// 创建人 : 逍遥剑客
/// 创建时间: 2006-10-29
/// -->
namespace ClassAdapterModel
... {
/**//// <summary>
/// 供了客户所需要的服务的类
/// </summary>
class ExistingClass
...{
public void UsefulMethod(string inputParam)
...{
Console.WriteLine("完成客户使需要的服务! 传入的参数为:" + inputParam);
}
}
/**//// <summary>
/// 客户定义的接口
/// </summary>
interface IRequiredInterface
...{
void RequiredMenthod(string inputParam);
}
/**//// <summary>
/// 创建一个新类,它继承提供服务的类,实现了客户的接口
/// </summary>
class NewClass : ExistingClass, IRequiredInterface
...{
public void RequiredMenthod(string inputParam)
...{
UsefulMethod(inputParam);
}
}
/**//// <summary>
/// 客户类,通过该类创建一个客户对象,它需要 ExistingClass 提供的服务
/// </summary>
class Client
...{
/**//// <summary>
/// 构造函数
/// </summary>
public Client()
...{
Console.WriteLine("构造函数!");
}
static void Main(string[] args)
...{
Client clt = new Client();
NewClass nc = new NewClass();
IRequiredInterface ir = nc;
ir.RequiredMenthod("客户类参数");
}
}
}
(2) 客户没有定义它需要提供服务的接口,但是有一个客户使用的类,另外存在一个类提供了客户所需要的服务。这时,我们适配器模式(Adapter)是:创建一个新类,它继承客户使用的类,然后利用提供服务的类创建用一个对象,新类通过使用这个对象的方法来完成所需要的服务。它简称对象适配器模式。代码如下:
using System.Collections.Generic;
using System.Text;
/**/ /// <!--
/// 创建人 : 逍遥剑客
/// 创建时间: 2006-10-29
/// -->
namespace ObjectAdapterModel
... {
/**//// <summary>
/// 供了客户所需要的服务的类
/// </summary>
class ExistingClass
...{
public void UsefulMethod(string inputParam)
...{
Console.WriteLine("完成客户使需要的服务! 传入的参数为:" + inputParam);
}
}
/**//// <summary>
/// 客户类,通过该类创建一个客户对象,它需要 ExistingClass 提供的服务
/// </summary>
class Client
...{
/**//// <summary>
/// 构造函数
/// </summary>
public Client()
...{
Console.WriteLine("构造函数!");
}
/**//// <summary>
/// 该类自身具有的功能
/// </summary>
/// <param name="inputParam">输入参数</param>
public virtual void RequiredMenthod(string inputParam)
...{
Console.WriteLine("该类自身具有的功能,但不足以完成任务! 传入的参数为:" + inputParam);
}
}
/**//// <summary>
/// 创建一个新类,它继承客户类
/// </summary>
class NewClass : Client
...{
private ExistingClass es = new ExistingClass();
/**//// <summary>
/// 新类通过使用创建提供服务类的对象的方法来完成所需要的服务
/// </summary>
/// <param name="inputParam">输入参数</param>
public override void RequiredMenthod(string inputParam)
...{
es.UsefulMethod(inputParam);
}
static void Main(string[] args)
...{
Client clt = new Client();
clt.RequiredMenthod("客户类参数");
}
}
}
(1)类适配器模式和(2)对象适配器模式比较,后者的设计方案非常脆弱。首先,因为后者在创建一个新类,继承客户使用的类,要求客户使用的类的方法标记为 virtual。如果没有标记,则在子类中无法覆盖(override)其方法。再次,如果后者的客户使用类的方法名可能改变,从而引起子类中的方法名称的变化。
所以,一般情况下,需要在一个接口中定义一个客户程序的要求,即使用类适配器模式。但是,如果无法预先定义客户需要的接口,那么也可以使用对象适配器模式,这时需要在客户使用类中用virtual标记可能在子类中被覆盖的方法。
另外提到一个简单的对.Net的数据进行适配的例子,但是它并没有使用适配器模式(Adapter)。这个适配器将数据库与显示数据的表格协同工作。下面代码仅提供一种思路,无法运行。我们可以通过创建一个类实现一个接口来替代这两条语句(g.DataSource = tb; g.DataBinding();),从而完成数据源的绑定,这采用了(1)中的类适配器模式,体现了绑定的灵活性。代码如下:
/// 使得 DataTable 与 GridView 协同工作
/// 代码无法运行,仅供理解使用
/// </summary>
public class DataAdapterExample
... {
public void DataAdapterExample()
...{
string strSql = "SELECT * FROM DataAdapterExample";
DataTable tb = new DataTable();
Database dbTemp = BasicOperate.CreateDataBase();
DbCommand cmdTemp = dbTemp.GetSqlStringCommand(strSql);
tb = dbTemp.ExecuteDataSet(cmdTemp).Tables[0];
GridView g = new GridView();
g.DataSource = tb;
g.DataBinding();
}
}
本文参考:
[1] 《C# 设计模式》《 Desing Patterns in C#》(美)Steven John Metsker 著, 颜炯 译。