一、分类:
结构型模式
二、理解“适配”
“适配”其实就是一种转换,这种转换发生在你不想改变某个东西现有功能,但又想把这个东西用在另外的一种新场合中。
绝大多数产品在设计之时是针对某个特定使用场合的,它向这个特定的使用场合公开一些接口以使客户可以使用它。一旦其使用场合发生变化,其对外公开的接口可能就不再符合客户的需求了。但与此同时,我们不想去改变原有的产品(如果你想改变这个原有产品的实现,那就不需要使用适配了),因为它拥有很好的功能且已经在使用过程中经过了验证,这个时候我们就需要“转化”(适配)一下,提供一个适配器,将原来产品的接口转化为客户期望的接口。这样的例子在生活中很容易举证,比如我们家庭中使用的很多电器所要求的电压为220,但也有一些电器要求更低的电压,这个时候就出现了电源适配器,利用电源适配器来改变输出的电压以提供给低电压的电器使用。
二、应用场景假设
其实上面的文件已经表示出了适配器模式使用的场合了。表现在软件设计与开发中,就是由于客户需求的变化,要将一些现有的对像放在一个新场合中使用,而新场合所期望的接口是这些原有对象所不能满足的,而我们又不想(在某些情况下可能是你根本不能去改变的)改变这些原有实现。
三、意图
将一个类的接口转换为客户希望的另一个接口。Adapter使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。(Gof)
四、结构
Adapter模式有两种结构,分别是类的适配与对象的适配。
1、类的适配


两种结构的角色其实都是一样的,客户的调用流程也是相同的。不同之处于两者在包装Adaptee源角色时,前者(类适配)包装的是Adaptee类(因为它同时从Target与Adaptee继承而来,可想而知,类适配的Adatper必须是一个具体类,而Target只能是一个接口),后者(对象适配)则直接包装了一个源Adaptee的实例。这一点如果放在代码中,则更容易体现出来。此处的差别导致了在具体实现时各个角色的不同实现方式(以类还是以接口)。
五、代码示例
以电压的转换为例
1、类适配
/// <summary>
/// 目标接口,这是客户要使用的,客户需要30伏的电压,这里必须是一个接口,因为C#不支持多重继承
/// </summary>
public interface Target
{
int Out();
}
/// <summary>
/// 标准电压类,提供220伏电压,这是现有对象(即源Adaptee)
/// </summary>
public class Tension
{
public Tension()
{
//...
}
/// <summary>
/// 输出电压
/// </summary>
/// <returns></returns>
public int OutTension()
{
return 220 ;
}
}
/// <summary>
/// 电源适配器类,同时继承Target与Tension(源Adaptee对象),这里必须是一个类
/// </summary>
public class PowerAdapter:Tension,Target
{
public PowerAdapter()
{
//...
}
#region Target 成员
public int Out()
{
//do something
//...
//call
return base.OutTension() - 190 ;
}
#endregion
}
客户调用:
Target t = new PowerAdapter() ;
Response.Write(t.Out().ToString()) ;
输出结果:
30
2、对象适配
对象适配直接封装了一个Adaptee实例,所以,Adapter适配器就可以不需要去继承Adaptee源对象了,只需要继承Target就可以了,这个时候由于Adapter只需要从单独的Target继承,所以,Target在实现时就不局限于只是接口的规定了,它也可以是一个类。我们面边还是把它写成了接口,这没关系。
/// <summary>
/// 目标接口,这是客户要使用的,客户需要30伏的电压,这里必须是一个接口,因为C#不支持多重继承
/// </summary>
public interface Target
{
int Out();
}
/// <summary>
/// 标准电压类,提供220伏电压,这是现有对象(即源Adaptee)
/// </summary>
public class Tension
{
public Tension()
{
//...
}
/// <summary>
/// 输出电压
/// </summary>
/// <returns></returns>
public int OutTension()
{
return 220 ;
}
}
/// <summary>
/// 电源适配器类,同时继承Target与Tension(源Adaptee对象),这里必须是一个类
/// </summary>
public class PowerAdapter:Target
{
private Tension MyTension = new Tension() ;
public PowerAdapter()
{
//...
}
#region Target 成员
public int Out()
{
//do something
//...
//call
return MyTension.OutTension() - 190 ;
}
#endregion
}
客户调用:
Target t = new PowerAdapter() ;
Response.Write(t.Out().ToString()) ;
程序输出:
30
通过对代码的分析不难发现,类适配使用的是多继承来实现适配工作,本身类继承可能会带来的一个结果就是“高耦合”,所以推荐使用对象的适配而不是类的适配,因为对象适配使用的是“对象组合”的方式,其带来的“低耦合”使系统在未来更容易扩展。
六、关于适配器模式的演化
在我们观察对象适配时,目标角色在某些情况下是完全可以被去掉了,客户只需要直接使用适配器对象就OK了。如下代码
/// <summary>
/// 标准电压类,提供220伏电压,这是现有对象(即源Adaptee)
/// </summary>
public class Tension
{
public Tension()
{
//...
}
/// <summary>
/// 输出电压
/// </summary>
/// <returns></returns>
public int OutTension()
{
return 220 ;
}
}
/// <summary>
/// 电源适配器类
/// </summary>
public class PowerAdapter
{
private Tension MyTension = new Tension() ;
public PowerAdapter()
{
//...
}
#region Target 成员
public int Out()
{
//do something
//...
//call
return MyTension.OutTension() - 190 ;
}
#endregion
}
客户调用:
PowerAdapter t = new PowerAdapter() ;
Response.Write(t.Out().ToString()) ;
程序输出:
30
这样做有一个好处就是如果Target规定了很多与Adaptee使用无关的接口,那我们可以决定我们不需要实现Target的所有规定,当然前提是Adapter已经知道了客户期望的接口并已将其实现。
此文来源:henry的空间 http://hi.baidu.com/sanlng/blog/category/%C9%E8%BC%C6%C4%A3%CA%BD/index/0