适配器模式的实现要点:
在适配器模式的结构图有以下角色:
(1)、目标角色(Target):定义Client使用的与特定领域相关的接口。
(2)、客户角色(Client):与符合Target接口的对象协同。
(3)、被适配角色(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。
(4)、适配器角色(Adapte) :适配器模式的核心。它将对被适配Adaptee角色已有的接口转换为目标角色Target匹配的接口。对Adaptee的接口与Target接口进行适配.
1、Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
2、GoF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。类适配器采用“多继承”的实现方式,在C#语言中,如果被适配角色是类,Target的实现只能是接口,因为C#语言只支持接口的多继承的特性。在C#语言中类适配器也很难支持适配多个对象的情况,同时也会带来了不良的高耦合和违反类的职责单一的原则,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神,对适配的对象也没限制,可以一个,也可以多个,但是,使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。Adapter模式可以实现的非常灵活,不必拘泥于GoF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。
3、Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便地适配。
适配器模式用来解决现有对象与客户端期待接口不一致的问题,下面详细总结下适配器两种形式的优缺点。
1】、类的适配器模式:
优点:
(1)、可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
(2)、可以重新定义Adaptee(被适配的类)的部分行为,因为在类适配器模式中,Adapter是Adaptee的子类
(3)、仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例(这个即是优点也是缺点)。
缺点:
(1)、用一个具体的Adapter类对Adaptee和Target进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模式中没有引入Adaptee的实例,光调用this.SpecificRequest方法并不能去调用它对应子类的SpecificRequest方法。
(2)、采用了 “多继承”的实现方式,带来了不良的高耦合。
2】、对象的适配器模式
优点:
(1)、可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”(这点是两种实现方式都具有的)
(2)、采用 “对象组合”的方式,更符合松耦合。
缺点:
(1)、使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
3】、适配器模式使用的场景:
(1)、系统需要复用现有类,而该类的接口不符合系统的需求
(2)、想要建立一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
(3)、对于对象适配器模式,在设计里需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
对象的适配器模式
A 类中有虚拟a方法 B类中有b方法
C类继承A类,且C类中含有B类的变量对象mB
C类重写a方法,在a方法内调用变量对象mB的b方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdapterSample
{
/// <summary>
/// 我家只有2个孔的插座,也就是适配器模式中的目标(Target)角色,这里可以写成抽象类或者接口
/// </summary>
public class TwoHoleTarget
{
// 客户端需要的方法
public virtual void Request()
{
Console.WriteLine("两孔的充电器可以使用");
}
}
/// <summary>
/// 手机充电器是有3个柱子的插头,源角色——需要适配的类(Adaptee)
/// </summary>
public class ThreeHoleAdaptee
{
public void SpecificRequest()
{
Console.WriteLine("我是3个孔的插头也可以使用了");
}
}
/// <summary>
/// 适配器类,TwoHole这个对象写成接口或者抽象类更好,面向接口编程嘛
/// </summary>
public class ThreeToTwoAdapter : TwoHoleTarget
{
// 引用两个孔插头的实例,从而将客户端与TwoHole联系起来
private ThreeHoleAdaptee threeHoleAdaptee = new ThreeHoleAdaptee();
//这里可以继续增加适配的对象。。
/// <summary>
/// 实现2个孔插头接口方法
/// </summary>
public override void Request()
{
//可以做具体的转换工作
threeHoleAdaptee.SpecificRequest();
//可以做具体的转换工作
}
}
class Program
{
///<summary>
///家里只有两个孔的插座,也懒得买插线板了,还要花钱,但是我的手机是一个有3个小柱子的插头,明显直接搞不定,那就适配吧
///</summary>
static void Main(string[] args)
{
//好了,现在就可以给手机充电了
TwoHoleTarget homeTwoHole = new ThreeToTwoAdapter();
homeTwoHole.Request();
Console.ReadLine();
}
}
}
类的适配器模式
接口A 类中有a方法 抽象 B类中有b方法
C类继承A类和B类,C类实现a方法,在a方法内调用来至于继承B的b方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdapterSample
{
/// <summary>
/// 我家只有2个孔的插座,也就是适配器模式中的目标角色(Target),这里只能是接口,也是类适配器的限制
/// </summary>
public interface ITwoHoleTarget
{
void Request();
}
/// <summary>
/// 3个孔的插头,源角色——需要适配的类(Adaptee)
/// </summary>
public abstract class ThreeHoleAdaptee
{
public void SpecificRequest()
{
Console.WriteLine("我是三个孔的插头");
}
}
/// <summary>
/// 适配器类,接口要放在类的后面,在此无法适配更多的对象,这是类适配器的不足
/// </summary>
public class ThreeToTwoAdapter : ThreeHoleAdaptee, ITwoHoleTarget
{
/// <summary>
/// 实现2个孔插头接口方法
/// </summary>
public void Request()
{
// 调用3个孔插头方法
this.SpecificRequest();
}
}
class Program
{
///<summary>
///家里只有两个孔的插座,也懒得买插线板了,还要花钱,但是我的手机是一个有3个小柱子的插头,明显直接搞不定,那就适配吧
///</summary>
static void Main(string[] args)
{
//好了,现在可以充电了
ITwoHoleTarget change = new ThreeToTwoAdapter();
change.Request();
Console.ReadLine();
}
}
}