参考链接:http://www.cnblogs.com/zhili/p/AdapterPattern.html
一、定义:
将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。
适配器模式有类的适配器模式和对象的适配器模式两种形式。
二、类的适配器模式实现(不推荐,违反了类的单一职责原则)
using System; /// 这里以插座和插头的例子来诠释适配器模式
/// 现在我们买的电器插头是2个孔,但是我们买的插座只有3个孔的
/// 这是我们想把电器插在插座上的话就需要一个电适配器
namespace 设计模式之适配器模式
{
/// <summary>
/// 客户端,客户想要把2个孔的插头 转变成三个孔的插头,这个转变交给适配器就好
/// 既然适配器需要完成这个功能,所以它必须同时具体2个孔插头和三个孔插头的特征
/// </summary>
class Client
{
static void Main(string[] args)
{
// 现在客户端可以通过电适配要使用2个孔的插头了
IThreeHole threehole = new PowerAdapter();
threehole.Request();
Console.ReadLine();
}
} /// <summary>
/// 三个孔的插头,也就是适配器模式中的目标角色
/// </summary>
public interface IThreeHole
{
void Request();
} /// <summary>
/// 两个孔的插头,源角色——需要适配的类
/// </summary>
public abstract class TwoHole
{
public void SpecificRequest()
{
Console.WriteLine("我是两个孔的插头");
}
} /// <summary>
/// 适配器类,接口要放在类的后面
/// 适配器类提供了三个孔插头的行为,但其本质是调用两个孔插头的方法
/// </summary>
public class PowerAdapter:TwoHole,IThreeHole
{
/// <summary>
/// 实现三个孔插头接口方法
/// </summary>
public void Request()
{
// 调用两个孔插头方法
this.SpecificRequest();
}
}
}
从上面代码中可以看出,客户端希望调用Request方法(即三个孔插头),但是我们现有的类(即2个孔的插头)并没有Request方法,它只有SpecificRequest方法(即两个孔插头本身的方法),然而适配器类(适配器必须实现三个孔插头接口和继承两个孔插头类)可以提供这种转换,它提供了Request方法的实现(其内部调用的是两个孔插头,因为适配器只是一个外壳罢了,包装着两个孔插头(因为只有这样,电器才能使用),并向外界提供三个孔插头的外观,)以供客户端使用。
PowerAdapter类同时继承TwoHole和IThreeHole,既可以使用TwoHole里的方法,也可以使用IThreeHole接口里的方法,这样就感觉有点不伦不类的,违反了类的单一职责原则,它既有TwoHole的职责,也有IThreeHole职责,因此这种类适配器不是很常用,也不推荐使用。
三、对象的适配器模式实现
namespace 对象的适配器模式
{
class Client
{
static void Main(string[] args)
{
// 现在客户端可以通过电适配要使用2个孔的插头了
ThreeHole threehole = new PowerAdapter();
threehole.Request();
Console.ReadLine();
}
} /// <summary>
/// 三个孔的插头,也就是适配器模式中的目标(Target)角色
/// </summary>
public class ThreeHole
{
// 客户端需要的方法
public virtual void Request()
{
// 可以把一般实现放在这里
}
} /// <summary>
/// 两个孔的插头,源角色——需要适配的类
/// </summary>
public class TwoHole
{
public void SpecificRequest()
{
Console.WriteLine("我是两个孔的插头");
}
} /// <summary>
/// 适配器类,这里适配器类没有TwoHole类,
/// 而是引用了TwoHole对象,所以是对象的适配器模式的实现
/// </summary>
public class PowerAdapter : ThreeHole
{
// 引用两个孔插头的实例,从而将客户端与TwoHole联系起来
public TwoHole twoholeAdaptee = new TwoHole(); /// <summary>
/// 实现三个孔插头接口方法
/// </summary>
public override void Request()
{
twoholeAdaptee.SpecificRequest();
}
}
}
如果一个方法有可能要委托到两个或两个以上的对象,或者两个或两个以上的类需要委托,对于对象适配器,只需要增加几个内部的属性就可以实现适配器模式了。而对于类适配器模式,因为C#中类只能是单一继承,它不能继承自两个或两个以上的类,所有类适配器无法使用。
四、适配器模式的优缺点
类的适配器模式:
优点:
可以在不修改原有代码的基础上来复用现有类,很好的符合“开闭原则”
可以重新定义Adapter的部分行为,因为在类适配器模式中,Adapter是引用Adapter的子类
仅仅引入一个对象,并不需要额外的字段来引用Adapter实例(这个即是优点也是缺点)。
缺点:
用一个具体的Adapter类对Adapter和Target进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模式中没有引入Adapter的实例,光调用this.SpecificRequest方法并不能去调用它对应子类的SpecificRequest方法。
采用了 “多继承”的实现方式,带来了不良的高耦合。
对象的适配器模式
优点:
可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”(这点是两种实现方式都具有的)
采用 “对象组合”的方式,更符合松耦合。
缺点:
使得重定义Adapter的行为较困难,这就需要生成Adapter的子类并且使得Adapter引用这个子类而不是引用Adapter本身。
五、使用场景
在以下情况下可以考虑使用适配器模式:
系统需要复用现有类,而该类的接口不符合系统的需求
想要建立一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
对于对象适配器模式,在设计里需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
六、.NET框架中的Adapter应用
1.在.NET中复用COM对象:
-COM对象不符合.NET对象的接口,使用tlbimp.exe来创建一个Runtime Callable Wrapper(RCW)以使其符合.NET对象的接口
2..NET数据访问类(Adapter变体):
-各种数据库并没有提供DataSet接口,使用DbDataAdapter可以将任何个数据库访问/存取适配到一个DataSet对象上
DbDataAdapter dataAdapter = new SqlDataAdapter();
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet);
微软把适配器和被适配的对象分离了,这和上面的模式有点不同,但是原理都是一样的,如果是写成下面的样子,就比较好理解了。
class DataSet
{
DataAdapter dataAdapter;
public DataSet(DbDataAdapter dataAdapter)
{
this.DataTable = dataAdapter.Fill();
}
}
DataSet就是一个适配器
DataSet dataSet = new DataSet(new SqlDataAdapter());
把适配对象直接作为参数传递一样是一种很好的做法。