适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。我准备用一个小例子来讲述这个适配器模式。
原型
本来是这样的,有个用USB连接的东西,突然有一天说增加了GIGE网口的连接方式,根据“开闭原则”我不想改变之前的USB类,同时,IUSB类的各种方法我们还是需要实现的,那么我们就需要用到适配器模式。
class Program
{
static void Main(string[] args)
{
IUSB usb = new USB();
usb.Connect();
Console.Read();
}
}
class USB : IUSB
{
public void Connect()
{
Console.WriteLine("USB连接成功");
}
}
interface IUSB
{
void Connect();
}
类图
类的适配器模式
当我们要访问的接口IUSB中没有我们想要的方法 ,却在另一个接口IGIGE中发现了合适的方法,我们又不能改变访问接口IUSB,在这种情况下,我们可以定义一个适配器ConnectAdapter类来进行中转,这个适配器ConnectAdapter类要实现我们访问的接口IUSB,这样我们就能继续访问当前接口IUSB中的方法,然后再继承接口IGIGE的实现类GIGE类,这样我们可以在适配器ConnectAdapter类中访问接口IGIGE的方法了,这时我们在适配器ConnectAdapter类中的接口IUSB方法中直接引用GIGE中的Connect方法,这样就完成了一个简单的类适配器。
class Program
{
static void Main(string[] args)
{
IUSB gige = new ConnectAdapter();
gige.Connect();
Console.Read();
}
}
class GIGE : IGIGE
{
public void GIGEConnect()
{
Console.WriteLine("GIGE连接成功");
}
}
class USB : IUSB
{
public void Connect()
{
Console.WriteLine("USB连接成功");
}
}
interface IGIGE
{
void GIGEConnect();
}
interface IUSB
{
void Connect();
}
class ConnectAdapter : GIGE, IUSB
{
public void Connect()
{
this.GIGEConnect();
}
}
类图
类的适配器模式的优点与缺点
类的适配器模式的优点:
- 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
- 可以重新定义Adaptee(被适配的类)的部分行为,因为在类适配器模式中,Adapter是Adaptee的子类
- 仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例(这个即是优点也是缺点)。
类的适配器模式的缺点:
- 用一个具体的Adapter类对Adaptee和Target进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模式中没有引入Adaptee的实例,光调用this.SpecificRequest方法并不能去调用它对应子类的SpecificRequest方法。
- 采用了 “多继承”的实现方式,带来了不良的高耦合。
对象的适配器模式
当我们要访问的接口IUSB中没有我们想要的方法 ,却在另一个接口IGIGE中发现了合适的方法,我们又不能改变访问接口IUSB,在这种情况下,我们可以定义一个适配器ConnectAdapter类来进行中转,这个适配器ConnectAdapter类要实现我们访问的接口IUSB,这样我们就能继续访问当前接口IUSB中的方法,定义一个带参数的构造器来构造注入,增加可测试性,再在IUSB接口的方法实现中使用私有的对象调用其来源于IGIGE接口的方法。
class Program
{
static void Main(string[] args)
{
IUSB gige = new ConnectAdapter(new GIGE());
gige.Connect();
Console.Read();
}
}
class GIGE : IGIGE
{
public void GIGEConnect()
{
Console.WriteLine("GIGE连接成功");
}
}
class USB : IUSB
{
public virtual void Connect()
{
Console.WriteLine("USB连接成功");
}
}
interface IGIGE
{
void GIGEConnect();
}
interface IUSB
{
void Connect();
}
class ConnectAdapter : USB
{
private readonly IGIGE _gige;
public ConnectAdapter(IGIGE gige)
{
this._gige = gige;
}
public override void Connect()
{
_gige.GIGEConnect();
}
}
类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景:
- 想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
- 我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。
类图
对象的适配器模式的优点与缺点
对象的适配器模式的优点:
- 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
- 采用 “对象组合”的方式,更符合松耦合。
对象的适配器模式的缺点:
- 使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
接口的适配器模式
这里为了更好的理解,稍微反一下,因为网口有设置IP的操作,而USB没有,但是IUSB这个接口已经定义了好多方法,你必须实现他,那么就可以首先先用一个类来继承IGIGE,空实现,然后再定义一个类重载IGIGE接口的对自己有用的方法,本例就是Connect方法。
class Program
{
static void Main(string[] args)
{
IGIGE usb = new USB();
usb.Connect();
Console.Read();
}
}
interface IGIGE
{
void Connect();
void SetIP();
}
abstract class ConnectAdapter : IGIGE
{
public virtual void Connect() { }
public virtual void SetIP() { }
}
class USB : ConnectAdapter
{
public override void Connect()
{
Console.WriteLine("USB连接成功");
}
}
类图
接口的适配器模式的优点和缺点
接口的适配器模式的优点:
- 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
- 采用接口的方式,更符合松耦合。
对象的适配器模式的缺点:
- 没有用到的接口的其他几个方法,它们的实现用的空实现
本文主要借鉴了《Gof设计模式》