设计模式-适配器模式
定义:将一个类的接口转换成希望的另外的接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。
如何将下图中两个图形对接在一起?
方式一:随意挑选左、右图中的一个修改,然后就可以拼接在一起了
方式二:再做一个图,能够让新的图即能跟左边的图拼接,又能跟右边的图拼接,如下中间绿色图形是新制作的图。
项目设计时左右两个图可能没有关联关系,各自是一个独立的模块,并且项目开发过程中 左、右 两图已经分别接入到了一些功能模块中,并且已经经过测试验证,当新需求需要两图共同协作时,修改任何一个图本身可能都是困难的,因为要修改一个功能的内部逻辑,并且原本已经接入这两个图的所有功能模块都需要同步的修改,那么涉及到的所有模块都要重新进行测试,这违背了设计模式的开闭原则。
所以方式一不是好的解决方案,如果选择方式二呢?
选择方式二,我们需要增加一个类作为中间对象,因为是新加的类,不对会现有系统产生影响,我们只需要在新需求的功能模块中将左右两图进行适配即可,已有功能不需要重新进行大规模的测试,即能满足新需求也避免了对现有系统的影响,所以方式二是好的选择。
开始引入实例,手机是如何充电的?
(1) 现有手机电源充电器的输出口常见的是 USB 的
(2) 手机充电口常见的是 Type-C、Micro USB、lightning
需要使用一个数据线将 手机电源充电器与 手机 连接在一起才能充电,这个数据线的作用就是适配器
我们代码要实现的功能就是将手机电源充电器输出的USB 数据 通过数据线转换为 Type-C、Micro USB、lightning 数据的输出,然后接入到手机
考虑到单一职责原则,一个适配器最好只有一个转换功能,需要三个适配器类分别实现,下面只展示 lightning 适配器的实现
手机电源充电器输出的 USB 数据定义如下
// USB 数据
public struct UsbData
{
// 电压
public float voltage;
// 电流
public float electricity;
}
手机输入需要的 Lightning 数据定义如下
// lightning 数据
public struct LightningData
{
// 电压
public float voltage;
// 电流
public float electricity;
}
定义 Usb 数据到 Lightning 数据的转换适配器接口
// 接口:USB 数据转换为 Lightning 数据
public interface IUSBToLightning
{
LightningData UsbToLightning(UsbData data);
}
适配器实现
public class UsbCableAdapter : IUSBToLightning
{
// 实现接口将USB 数据转换为 Lightning 数据
public LightningData UsbToLightning(UsbData data)
{
LightningData lightningData = new LightningData();
lightningData.voltage = data.voltage;
lightningData.electricity = data.electricity;
return lightningData;
}
}
调用如下
class Customer
{
public Customer()
{
// USB 数据
UsbData usbData = new UsbData();
usbData.voltage = 5;
usbData.electricity = 3;
// Usb 转换 LightningData 适配器
IUSBToLightning iUSBToLightning = new UsbCableAdapter();
LightningData lightningData = iUSBToLightning.UsbToLightning(usbData);
Console.WriteLine(lightningData.voltage + " " + lightningData.electricity);
}
}
优点:
(1) 在不修改原有代码的基础上复用现有类,符合开闭原则
(2) 灵活性好,增删适配器方便
缺点:
(1) 如果要匹配一个类以及所有它的子类时,类的适配器就不能胜任了