13->The Adapter Pattern(infi_image预定)
时间:2006-03-27
来源:互联网
The Adapter
Pattern
INTERFACES CHANGE. It’s a simple and perennial fact that programmers have to (albeit grudgingly)
accept and contend with. Vendors change their code; system libraries are revised; and programming
languages and their incumbent libraries evolve. One of my son’s countless toys succinctly
describes the dilemma: you can’t fit a square peg in a round hole.
The Problem
How can you protect yourself from changes in the API of external libraries you use? If you write a
library, can you provide a means to allow existing users of your software to seamlessly upgrade, even
if you’ve changed your API? How can you change the interface of an object to better suit your needs?
The Solution
The Adapter pattern provides a different interface to an object. You can use an Adapter to realize a
familiar interface to a different object, avoiding the hassle of updating or refactoring your client code.
作者: PHPChina 发布时间: 2006-03-26
作者: Bantu 发布时间: 2006-03-28
作者: infi_image 发布时间: 2006-03-29
作者: PHPChina 发布时间: 2006-03-29
作者: infi_image 发布时间: 2006-03-29
接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题。程序提供者们修改他们的代码;系统库被修正;各种程序语言以及相关库的发展和进化。我孩子的无数玩具中有一个简要地描述了这个两难局面:你无法合理安排一个不得其所的人。
问题
你如何避免因外部库的API改变而带来的不便?假如你写了一个库,你能否提供一种方法允许你软件的现有用户进行完美地升级,即使你已经改变了你的API?为了更好地适宜于你的需要,你应该如何改变一个对象的接口?
解决方案
适配器(Adapter)模式为对象提供了一种完全不同的接口。你可以运用适配器(Adapter)来实现一个不同的类的常见接口,同时避免了因升级和拆解客户代码所引起的纠纷。
考虑一下当(不是假设!)一个第三方库的API改变将会发生什么。过去你只能是咬紧牙关修改所有的客户代码,而情况往往还不那么简单。你可能正从事一项新的项目,它要用到新版本的库所带来的特性,但你已经拥有许多旧的应用程序,并且它们与以前旧版本的库交互运行地很好。你将无法证明这些新特性的利用价值,如果这次升级意味着将要涉及到其它应用程序的客户代码。
控制体模式
适配器(Adapter)模式是控制体模式的最新范例。一个适配器(Adapter)的结构类似于代理服务器(Proxy)和修饰器(Decorator),而它们的不同之处在于,适配器(Adapter)的目的是改变封装类的接口,代理服务器(Proxy)和修饰器(Decorator)则是保持接口不变。
代码范例
让我们看看当API改变时,如何保护应用程序不受影响。
假设你费尽心思寻找合适的库,最后终于找到了HwLib,一个(假设的)被设计用来发送信息的代码集。
下面是HwLib类的源代码:
//代码片断//
下面是库运行的范例:
//代码片断//
HwLib有完备的说明文档。并且作者已经明确地指出hello()方法会在未来的版本中不被支持(甚至淘汰)。
接下来,现在假设第二版的HwLib已经发布。一个全新的greet()方法代替了hello()。
下面是这个库的新版本(注释已被抽取掉):
//代码片断//
为了适应HwLib的不同版本进行编码,先进行一些基于第一版本HwLib接口的测试:
//代码片断//
你同样可以表明,对这个库的简单升级将造成此应用程序的失效。
//代码片断//
(这个测试以method_exists()为例证。如果你简单地更换这个库的第二版本并且以TestOriginalApp()的测试再次运行AdapterTestCase,PHP就会运行失败,同时报告“致命错误:未定义的函数:hello().“)
针对API“升级”的解决办法就是创建一个适配器(Adapter)。
第一步是获得第二版本HwLib的实例的一个引用,并且把它加入到你的Adapter类中。
//代码片断//
这个范例展示了将这个实例传递给构造函数的过程,你也可以运用Factory 或 Singleton ,或者其它适合你要求的创建模式,来创建一个新的实例。(通过前两章,你应该对HwLibV2ToV1Adapter的编写用途很熟悉了)
当得到第二版本HwLib的时候,你如何使它在第一版本HwLib的实例中体现?
//代码片断//
HwLibV2ToV1Adapter::hello()方法代表$libv2对象的greet()方法。
接着,你该如何在程序中使用它?
//代码片断//
刚才的程序测试和现在的程序代码都有一定程度的脆弱性。有没有方法在长久的使用中让它们都更易于维护呢?当然是有的!
回忆一下(第三章)Factory是如何提供一个更灵活的方法来创建类的实例的。为了更好地在将来检验这些代码,就从一个简单的Factory函数开始:
//代码片断//
为了测试Factory,直接调用它而不创建它的实例。
//代码片断//
有两个方面需要注意:Factory创建了对象,而用于确认的assertEqual()函数被修改为更灵活的assertWantedPattern()。你现在可以用一个正则表达式来捕获你在库中所要查找的“核心”,但可能会使这个测试本身变得脆弱。
接下来,升级HwLib库。当安装了HwLib第二版,你就可以修改HwLibInstance()函数来适应新的版本。
//代码片断//
现在重新运行AdapterTestCase。测试通过!(绿色进度条正常。)因为原始的程序没有传递一个参数,HwLibInstance会默认返回封装在HwLibV2toV1Adapter中的HwLib的一个实例。尽管如此,如果你编写了新的代码,你可以传递进一个“V2”的参数让这个函数自动选择HwLib的新版本而不用去调整它。
以后,如果你选择升级HwLib的第三版,应该将Factory做如下的调整:
//代码片断//
结束语
像例中代码所示,你可以运用适配器(Adapter)模式来避免因外部库改变所带来的不便――倘若向上兼容。作为某个库的开发者,你应该独立编写适配器,使你的用户更简便地使用新版本的库,而不用去修改他们现有的全部代码。
GoF书中提出的适配器(Adapter)模式更倾向于运用继承而不是组成。这在强类型语言中是有利的,因为适配器(Adapter)事实上是一个目标类的子类,因而能更好地与类中方法相结合。
下面是HwLib适配器运用继承的范例:
//代码片断//
world()方法没有在类中提到,因为运用了继承,它已经是子类的一部分。
//代码片断//
为了更好的灵活性,我个人比较倾向于组成的方法(特别是在结合了依赖性倒置的情况下);尽管如此,继承的方法提供两种版本的接口,或许在你的实际运用中反而是一个灵活性的关键。
依赖性倒置原理
依赖性倒置原理(首先在http://www.objectmentor.com/resources/articles/dip.pdf中由Robert C. Martin提出)是一个面向对象编程的准则,它表明:高层次的模块不应该依赖于低层次的模块,而应依赖于抽取。一个简单的与适配器(Adapter)模式相结合的依赖性倒置原理范例可以在下面的地址中找到:http://www.phplondon.org/wiki/DependencyInversion
适配器模式的重点是改变一个单独类的API。有一个与之相关的设计模式(本书中没有涵盖),称作正面(Facade)模式。正面(Facade)的目的是给由许多对象构成的整个子系统,提供更为简洁的接口――反过来就是封装一个单独类――可能是一个值得研究的模式,如果你正设法把你的代码与第三方库隔离开来的话。
作者: infi_image 发布时间: 2006-03-29
作者: PHPChina 发布时间: 2006-03-30
作者: Nancily 发布时间: 2006-04-17
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28