4.1 场 景 问 题
4.1.1 装配电脑的例子
1.旧的硬盘和电源
小李有一台老的台式电脑,硬盘实在是太小了,仅仅40GB,但是除了这个问题外,整机性能还不错,废弃不用太可惜了,于是决定去加装一块新的硬盘。
在装机公司为小李的电脑加装新硬盘的时候,小李也在旁边观看,顺便了解一些硬件知识。很快,装机人员把两块硬盘都安装好了,细心的小李发现,这两块硬盘的连接方式是不一样的。
经过装机人员的耐心讲解,小李搞清楚了它们的不同。以前的硬盘是串口的,如图4.1所示,电脑电源如图4.2所示,在连接电源的时候是直接连接。
图4.1 旧的硬盘
图4.2 电脑电源
2.加入新的硬盘
现在的新硬盘是并口的,如图4.3所示,电源的输出口无法直接连接到新的硬盘上了。于是就有了转接线,一边和电源的输出口连接,一边和新的硬盘电源输入口连接,解决了电源输出接口和硬盘输入接口不匹配的问题,如图4.4所示。
图4.3 新的硬盘
图4.4 电源转接线
3.有何问题
如果把上面的问题抽象一下,用对象来描述,那就是:有一个电源类和旧的硬盘类配合工作得很好,现在又有了一个新的硬盘类,现在想让新的硬盘类和电源类也配合使用,但是发现它们的接口无法匹配,问题就产生了:如何让原有的电源类的接口能够适应新的硬盘类的电源接口的需要呢?
4.如何解决
解决方法是采用一个转接线类,转接线可以把电源的接口适配成为新的硬盘所需要的接口,那么这个转接线类就类似本章的主角——适配器(Adapter)。
4.1.2 同时支持数据库和文件的日志管理
看了上面这个例子,估计对适配器模式有一点感觉了。这是一个在生活中常见的例子,类似的例子很多,比如,各种管道的转接头、不同制式的插座等。但是这种例子只能帮助大家理解适配器模式的功能,跟实际的应用系统开发总是有一些差距,让人感觉到好像是理解了模式的功能,但是一到真实的系统开发中,就不知道如何使用这个模式了,有些隔靴搔痒的感觉。因此,下面还是以实际系统中的例子来讲述,以帮助大家真正理解和应用适配器模式。
考虑一个记录日志的应用,由于用户对日志记录的要求很高,使得开发人员不能简单地采用一些已有的日志工具或日志框架来满足用户的要求,而需要按照用户的要求重新开发新的日志管理系统。当然这里不可能完全按照实际系统那样去完整实现,只是抽取跟适配器模式相关的部分来讲述。
1.日志管理第一版
在第一版的时候,用户要求日志以文件的形式记录。开发人员遵照用户的要求,对日志文件的存取实现如下。
(1)先简单定义日志对象,也就是描述日志的对象模型。由于这个对象需要被写入文件中,因此这个对象需要序列化。示例代码如下:
(2)接下来定义一个操作日志文件的接口。示例代码如下:
(3)实现日志文件的存取。现在的实现也很简单,就是读写文件。示例代码如下:
(4)写个客户端来测试一下,看看好用不。示例代码如下:
测试的结果如下:
至此就简单的实现了用户的要求,把日志保存到文件中,并能从文件中把日志内容读取出来,进行管理。
看上去很容易,对吧,别慌,接着来。
2.日志管理第二版
用户使用日志管理第一版一段时间后,开始考虑升级系统,决定要采用数据库来管理日志。很快,按照数据库的日志管理也实现出来了,并定义了日志管理的操作接口,主要是针对日志的增删改查方法。接口的示例代码如下:
对于使用数据库来保存日志的实现,这里就不去涉及了,总之知道有这么一个实现就可以了。
客户提出了新的要求,能不能让日志管理第二版实现同时支持数据库存储和文件存储两种方式?
4.1.3 有何问题
有朋友可能会想,这有什么困难的呢,两种实现方式不是都已经实现了的吗,合并起来不就可以了?
问题就在于,现在的业务是使用的第二版的接口,直接使用第二版新加入的实现是没有问题的,第二版新加入了保存日志到数据库中;但是对于已有的实现方式,也就是在第一版中采用的文件存储的方式,它的操作接口和第二版不一样,这就导致现在的客户端无法以同样的方式来直接使用第一版的实现,如图4.5所示。
图4.5 无法兼容第一版的接口示意图
这就意味着,要想同时支持文件和数据库存储两种方式,需要再额外地做一些工作,才可以让第一版的实现适应新的业务需要。
可能有朋友会想,干脆按照第二版的接口要求重新实现一个文件操作的对象不就可以了吗,这样做确实可以,但是何必要重新做已经完成的功能呢?应该想办法复用,而不是重新实现。
一种很容易想到的方式是直接修改已有的第一版的代码。这种方式是不太好的,如果直接修改第一版的代码,那么可能会导致其他依赖于这些实现的应用不能正常运行,再说,有可能第一版和第二版的开发公司是不一样的,在第二版实现的时候,根本拿不到第一版的源代码。