设计模式(C#)——05适配器模式
推荐阅读:
自然界有一条规则——适者生存。意思是生物要使用自然界的变化;在程序界中则需要新环境调用现存对象。那么,如何在新环境中调用“现存的对象”呢?采用适配器模式可以解决。适配器模式使得新环境中不需要去重复实现已经存在了的实现,而很好的把现有的对象(指原来环境中的现有对象)加入到新环境中来使用。
适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法一起工作的两个类能够在一起工作。
适配器模式要素:
1、抽象目标(Target):抽象目标类定义客户所需的接口,可以是一个抽象类或接口,也可以是具体类。在类适配器中,由于C#语言不支持多重继承,所以它只能是接口;
2、适配器(Adapter)“它可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,它是适配器模式的核心;
3、适配者(Adaptee):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类包好了客户希望的业务方法。
适配器模式有类的适配器模式和对象的适配器模式两种形式。
下面举两个例子来带大家学习适配器模式。
在生活中,我们买电器的时候买了一个二孔的插头,但不幸的是回到家发现家里只有三孔的插座,这个时候怎么办呢?我们希望有个东西能连接电器和插头。这个东西就是适配器。命名空间适配器模式包含ClassBased和ObjectBased子命名空间,分别表示基于类的适配器模式和基于对象的适配器模式。
一。类的适配器模式
类的适配器中包含家用电器类、电压类、目标动作接口和手机适配器类。对象的适配器中包含适配者、适配器、目标接口和目标基类。
1.创建电脑IComputer,也就是适配器模式中的目标角色
public interface IThreeHole
{
void Request();
}
2.创建手机,源角色——需要适配的类
public abstract class TwoHole
{
public void SpecificRequest()
{
Console.WriteLine("我是两个孔的插头");
}
}
3.适配器
/// 适配器类,接口要放在类的后面
/// 适配器类提供了三个孔插头的行为,但其本质是调用两个孔插头的方法
/// </summary>
public class PowerAdapter:TwoHole,IThreeHole
{
/// <summary>
/// 实现三个孔插头接口方法
/// </summary>
public void Request()
{
// 调用两个孔插头方法
this.SpecificRequest();
}
}
4.使用适配器
static void Main(string[] args)
{
// 现在客户端可以通过电适配要使用2个孔的插头了
IThreeHole threehole = new PowerAdapter();
threehole.Request();
Console.ReadLine();
}
5.整合后的代码:
using System;
/// 这里以插座和插头的例子来诠释适配器模式
/// 现在我们买的电器插头是2个孔,但是我们买的插座只有3个孔的
/// 这是我们想把电器插在插座上的话就需要一个电适配器
namespace 适配器模式
{
/// <summary>
/// 客户端,客户想要把2个孔的插头 转变成三个孔的插头,这个转变交给适配器就好
/// 既然适配器需要完成这个功能,所以它必须同时具体2个孔插头和三个孔插头的特征
/// </summary>
class Program
{
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();
}
}
}
类的适配器模式
优点:
可以在不修改原有代码的基础上,很好的符合“开闭原则”
可以重新定义Adaptee(被适配的类)的部分行为,因为在类适配器模式中,Adapter是Adaptee的子类
仅引入一个对象,并不需要额外的字段来引用Adaptee实例
缺点:
用一个具体的Adapter类对Adaptee和Target进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模式中没有引入Adaptee的实例,光调用this.SpecificRequest方法并不能去调用它对应子类的SpecificRequest方法
采用了 “多继承”的实现方式,带来了不良的高耦合。
二。对象的适配器模式
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();
}
}
}
对象的适配器模式
优点:
可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”(这点是两种实现方式都具有的)
采用 “对象组合”的方式,更符合低耦合。
缺点:
使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
使用场景
在以下情况下可以考虑使用适配器模式:
系统需要复用现有类,而该类的接口不符合系统的需求
想要建立一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
对于对象适配器模式,在设计里需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
总结
适配器可以是抽象类,并适配器模式的实现是非常灵活的,我们完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,适配器类可以根据参数参数可以返回一个合适的实例给客户端。类的适配器模式是定义一个接口来实现客户端想要的方法,并在适配器类中继承这个接口和源角色。对象的适配器模式是在适配器类中继承要适配的类并声明一个源角色的对象,通过重写调用基类的方法来实现。