在前面一篇博客中介绍了“命令模式”(Command Pattern),今天博客的主题是“适配器模式”(Adapter Pattern)。适配器模式用处还是比较多的,如果你对“适配器模式”理解呢,那么自然而然的就会知道其使用场景。今天这篇博客我们就举一个生活中的使用适配器模式的示例,然后将该示例使用代码进行表示出来,那么我们所实现的代码使用的设计模式就是我们今天所说的“适配器模式”。
“适配器”其实说白了就是一个转接头,比如Mac Book Pro 要接外接显示器,如果你没有HDMI传输线的话,那么你就得使用转接头作为中间件来连接外接显示器。你观察各种“转接头”他们都有一个共同的特点,就是转接头两端的接口肯定是不一样的。其实上面我说了句废话了,如果转接头的两端一致那么就是传输线了就不是转接头了。其实我们今天所要介绍的“适配器模式”就来源生活中的各种适配器。适配器说白了,其职责就是将一种接口转换成另一种接口。下方是生活中常见的适配器。
上面说这么多了,那么让我们来看一下“适配器模式”的定义吧,下方是适配器模式的定义:
适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间
接下来我们就拿生活中的一个示例来真正的感受一下适配器模式。作为一个Programer你肯定使用过笔记本电脑,在此就使用MacBook Pro的电源适配器来做类比。MacBook Pro中接入外接电源时需要电源适配器的,接下来我们就使用代码来实现插座,适配器以及MacBook Pro和其自带电池间的关系。
一、电源适配器中的类图
接下来我们将给出MacBook Pro电源适配器中的使用代码表示的类图,也就是我们使用“适配器模式”来设计电源适配器时所使用的类图。下方就是我们所使用的类图,总体看来下方的类图分为三个模块,左边的红框是我们的插座,中间的黄框是我们的电源适配器,右边的绿框就是我们经常使用的笔记本电脑了。在笔记本电脑中带有电池,MacBook Pro使用自带电池是不需要适配器的,因为该电池在设计之初就是为其设计的。但是如果MacBook Pro的电池没有电了,我们就需要为其接入外部电源。因为外部电源是多种多样的,电压电流都有所不同,不能直接给MacBook Pro供电,所以需要电源适配器来为MacBook Pro供电。下图中黄色的就是我们的电源适配器,也是适配器模式的核心。依次给出了每个模块的的介绍,如下所示:
在下方类图中的红色部分就是我们模拟插座的模块。在该模块中含有一个SocketType协议,该协议是Socket(插座)的一种规范,在此我们可以暂且理解为插座的国标。其中定义了插座的统一规范,只给出了声明没有实现(只给出了插座的尺寸,没有给出如何生产插座)。我们要实现的Socket都遵循于SocketType协议,也就是说在中国生产的插座要符合中国国标,在美国生产的插座当然要符合美国的国标,当然这两者的插座标准有可能是一样的。红框中就模拟了我们的插座。
接下来我们来看一下绿框中的内容。绿框中就是我们的MacBook Pro,其中也有个协议,名为ComputerPowerSourceType。该协议是笔记本电脑????的供电系统协议,只要是遵循该协议(接口)的电源都可以给MacBook Pro供电。因为从下方的类图我们可以看出,MacBook Pro是依赖于供电系统的协议的而不是依赖于具体实现,所以笔记本电脑的自带电池是可以进行更换的,而且可以外接电源。但是外接电源也得必须要遵循MacBook Pro中的ComputerPowerSourceType协议。接下来就是适配器出厂的时候了。
要使用插座给MacBook Pro供电必须得用适配器,你不能直接拿根线捅上完事儿,这样会出问题的。接下来就是电源适配器出场的时候了,下方黄框中的就是电源适配器。当然黄框中一个类是一个适配器,也就是黄框中是两个适配器。不过这两个适配器实现方式不同,也就是不同类型的适配器,但都可以连接插座给MacBookPro进行供电。两者的具体不同之处会在具体实现的时候给出详细的解释。无论使用哪个电源适配器,我们的MacBook Pro就可以接上插座来工作了。这两个适配器因为一方要连接MacBookPro,所以都实现了ComputerPowerSourceType协议,这样适配器的一端连接MacBook Pro就不成问题了。除了连接MacBook Pro外,你还得对插座输出的电压等进行处理,从而MacBook Pro可以使用处理后的电压电流等。因为初始电流是插座提供的,所以适配器为了处理电压和电流还需要还需要继承或者依赖于插座。依赖插座的适配器我们称之为“对象适配器”(插座是适配器中的一个对象),继承自插座的适配器我们称之为“类适配器”(插座是适配器中的父类)。具体请看下方的代码实现。
二、电源适配器的具体实现
接下来就到了我们代码实现的时候了,还是那句话,只要思路有了实现应该是问题不大的。上面的类图就是我们的思路,我们在代码实现时,就是将上面的类图转换成具体的代码实现。当然本篇博客仍然使用的是Swift语言来实现的,当然你也可以使用其他你熟悉的任何面向对象语言来实现。接下来就一步步的将上述类图转变成具体的代码实现。下方先给出了插座模块的实现,然后给出了MacBook Pro的实现,最后给出了两种电源适配器的实现,具体请参见下方内容。
1.插座模块的实现
我们先对插座模块进行实现,对应着上方类图中红色的部分,也就是对SocketType协议以及Socket类进行具体代码实现,当然在实现Socket类时要遵循SocketType协议。SocketType协议中的socketOutputVoltage() -> Float 负责电压输出的,为了简化我们的代码复杂度,将重点放在适配器上,在此我们只实现了socketOutputVoltage() -> Float这一个方法,其余插座的方法我们在此进行省略掉。
下方代码中就是插座模块的具体实现,Socket(插座)遵循了SocketType(插座协议),并且实现了SocketType中的socketOutputVoltage()方法。在该方法中返回了插座的输出电压,我们常用的插座输出的电压是220.0V,所以在该函数中就返回220.0,具体实现如下所示:
2.MacBook Pro的代码实现
在下方代码中是对MacBook Pro模块的实现,对应这上方类图中的绿色部分。下方首先我们给出类计算机电源的接口协议,也就是ComputerPowerSourceType。凡是给MacBook Pro供电的设备都必须遵循这个协议,该协议中的outputVoltage() -> Float所提供的电压就是我们MacBook Pro所需的电压。我们的MacBook Pro的实现类依赖于ComputerPowerSourceType协议。
紧接着我们给出了MacBook Pro的自带电池的实现,即MacBookProBattery类。MacBookProBattery类遵循与ComputerPowerSourceType协议,这样这块电池才能给MacBook Pro供电。在MacBookProBattery类的outputVoltage()方法中返回了MacBook Pro所需的电压,如下代码段所示。
下方代码段的最下方的类是我们的MacBook Pro类,其中依赖于ComputerPowerSourceType协议。也就是说只要是遵循ComputerPowerSourceType协议的电源都可以给我们的计算机供电。在MacBookPro中的inputVoltage()方法中调用了实现ComputerPowerSourceType协议的对象的outputVoltage()方法,如下所示。
3. 电源适配器的代码实现
如果没有适配器,MacBookPro就只能使用MacBookProBattery(自带电池),而无法使用Socket(插座),要想MacBookPro也可以使用Socket进行供电就必须有“适配器”进行转换。上面两部分都是为该部分准备的,如果要将上面两部分进行连接,也就是使用插座给MacBook Pro进行供电,就需要本部分的适配器。该部分是“适配器模式”的核心,该部分是代码实现也是根据上述类图中黄色的部分进行实现的。在该部分会分别给出“类适配器”与“对象适配器”,具体内容请看下方详细代码片段。
下方的适配器都有一个共同点,因为其是笔记本电脑的适配器所以都遵循了ComputerPowerSourceType协议。也就是说他们都可以插入MacBook Pro进行供电,这是两个适配器一致的地方。至于该适配器可以插入哪一种插座,那么要看起适配器类型了。
(1). “对象适配器”的代码实现
之所以称之为“对象适配器”,是因为该适配器中还有一个插座的对象。该插座的对象是通过SocketType来声明的,也就是说这个插座对象可以是多种类型的插座,但前提是这些插座必须遵循SocketType协议即可。进一步说,“对象适配器”依赖于插座的接口而不依赖于插座的具体实现,这一点需要注意,因为这一点与“类适配器”不同,下方会给出比较。
在“对象适配器”MacPowerObjectAdapter中的insertSocket()的方法就是为该适配器指定特定的插座的,也是适配器插入插座的过程,这个插座只需要符合SocketType协议即可。在该适配器中还实现了ComputerPowerSourceType协议中的outputVoltage()方法,适配器就是通过该方法给MacBook Pro供电的。在该方法中对插座提供的电压进行了处理,使其可以适用于我们的MacBook Pro,具体实现如下所示:
(2). "类适配器"的代码实现
上面给出了“对象适配器”的实现,紧接着我们还会给出“类适配器”的代码实现。上面已经提到,对象适配器与类适配器都实现了ComputerPowerSourceType协议。但其两者的不同在于对象适配器是依赖于SocketType接口,也就是依赖于某个Socket对象,所以称为对象适配器。而类适配器则是继承在某个特定的插座的类,也就是“类适配器”依赖于Socket类的具体实现,所以称为“类适配器”
换句话说,就是“对象适配器”依赖于接口,而“类适配器”依赖于具体实现。所以根据我们之前所说的设计模式规则中的“依赖于接口,而不依赖于具体实现”的规则就可以看出那种适配器的可扩展性更好一些。下方的MacPowerClassAdapter适配器就是“类适配器”,该类适配器不仅仅遵循了ComputerPowerSourceType协议,还继承了Socket类,如下所示。
三、测试用例
根据我们的类图,经过上面这部分,我们给出了具体的代码实现。在该代码实现中我们分别给出了插座接口以及插座的实现、MacBook Pro以及电池和电池接口的实现、两种适配器的实现。接下来测试的时间到了,下方给出了我们该如何使用适配器在MacBook Pro和插座间进行对接。
1. 创建各种实例
首先我们先创建我们测试用例所需的实例对象,下方代码段创建了MacBook Pro的对象macBookPro,MacBook Pro所用电池的对象macBookProBattery,插座的对象socket,以及“对象适配器”的对象macBookProObjectAdapter和“类适配器”的对象macBookProClassAdapter。如下所示:
2. 使用上述对象
上面已经创建了我们测试用例所需要的所有对象,接下来让我们来进行测试。首先我们先使用电池来为我们的MacBook供电,如下所示:
使用MacBook Pro自带的电池是不需要使用我们定义的适配器的,不过当电池没电时,我们需要接入外接电源,此时我们需要使用我们的适配器了。下方使用了我们的对象适配器。先将适配器接入插座(为适配器指定插座对象),然后MacBook Pro接入适配器,然后就可以供电了。使用方式如下所示:
接下来是类适配器出场的时候了,下方的代码是使用“类适配器”的内容。因为类适配器以及确定要使用哪种类型的插座,所以不需要为其指定插座的特定类型。代码如下:
3. 测试用例输出结果
从输出结果可以看出,无论通过哪种方式供的电,虽然插座提供的电压是220V, 但是我们的MacBook Pro所获取的电压都是16.5,如下所示。
至此今天的博客就写完了,仔细阅读完本篇博客后我们应该对“适配器模式”有深入的了解。
今天博客中的代码仍然会在Github上进行分享,分享地址为:https://github.com/lizelu/DesignPatterns-Swift