1、1. 概述:接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题。程序提供者们修改他们的代码;系统库被修正; 各种程序语言以及相关库的发展和进化。例子 1:iphone4,你即可以使用 UBS 接口连接电脑来充电,假如只有 iphone 没有电脑,怎么办呢?苹果提供了 iphone 电源适配器。可以使用这个电源适配器充电。这个iphone 的电源适配器就是类似我们说的适配器模式。(电源适配器就是把电源变成需要的电压,也就是适配器的作用是使得一个东西适合另外一个东西。)例子 2:最典型的例子就是很多功能手机,每一种机型都自带有从电器,有一天自带充电器坏了,而且市场没有这类型充电
2、器可买了。怎么办?万能充电器就可以解决。这个万能充电器就是适配器。2. 问题你如何避免因外部库的 API 改变而带来的不便?假如你写了一个库,你能否提供一种方法允许你软件的现有用户进行完美地升级,即使你已经改变了你的 API?为了更好地适宜于你的需要,你应该如何改变一个对象的接口?3. 解决方案适配器(Adapter) 模式为对象提供了一种完全不同的接口。你可以运用适配器(Adapter) 来实现一个不同的类的常见接口,同时避免了因升级和拆解客户代码所引起的纠纷。适配器模式(Adapter Pattern),把一个类的接口变换成客户端所期待的另一种接口, Adapter 模式使原本因接口不匹配
3、(或者不兼容)而无法在一起工作的两个类能够在一起工作。又称为转换器模式、变压器模式、包装(Wrapper)器模式(把已有的一些类包装起来,使之能有满足需要的接口)。考虑一下当(不是假设!)一个第三方库的 API 改变将会发生什么。过去你只能是咬紧牙关修改所有的客户代码,而情况往往还不那么简单。你可能正从事一项新的项目,它要用到新版本的库所带来的特性,但你已经拥有许多旧的应用程序,并且它们与以前旧版本的库交互运行地很好。你将无法证明这些新特性的利用价值,如果这次升级意味着将要涉及到其它应用程序的客户代码。4. 分类共有两类适配器模式:1.类的适配器模式(采用继承实现) 2.对象适配器(采用对象组
4、合方式实现)1)类适配器模式 适配器继承自已实现的类(一般多重继承)。Adapter 与 Adaptee 是继承关系1、用一个具体的 Adapter 类和 Target 进行匹配。结果是当我们想要一个匹配一个类以及所有它的子类时,类 Adapter 将不能胜任工作2、使得 Adapter 可以重定义 Adaptee 的部分行为,因为 Adapter 是 Adaptee 的一个子集3、仅仅引入一个对象,并不需要额外的指针以间接取得 adaptee 2)对象适配器模式 适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。Adapter 与 Adaptee 是委托关系1、允
5、许一个 Adapter 与多个 Adaptee 同时工作。Adapter 也可以一次给所有的 Adaptee添加功能2、使用重定义 Adaptee 的行为比较困难无论哪种适配器,它的宗旨都是:保留现有类所提供的服务,向客户提供接口,以满足客户的期望。即在不改变原有系统的基础上,提供新的接口服务。5. 适用性以下情况使用 Adapter 模式:1 你想使用一个已经存在的类,而它的接口不符合你的需求。2 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。3 (仅适用于对象 Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子
6、类化以匹配它们的接口。对象适配器可以适配它的父类接口。即仅仅引入一个对象,并不需要额外的指针以间接取得 adaptee。 6. 结构类适配器使用多重继承对一个接口与另一个接口进行匹配,如下图所示:对象匹配器依赖于对象组合,如下图所示:7. 构建模式的组成目标角色(Target): 定义 Client 使用的与特定领域相关的接口。 客户角色(Client ):与符合 Target 接口的对象协同。 被适配橘色(Adaptee) :定义一个已经存在并已经使用的接口,这个接口需要适配。 适配器角色(Adapte) :适配器模式的核心。它将对被适配 Adaptee 角色已有的接口转换为目标角色 Tar
7、get 匹配的接口。对 Adaptee 的接口与 Target 接口进行适配.8. 效果类适配器和对象适配器有不同的权衡。类适配器 用一个具体的 Adapter 类对 Adaptee 和 Target 进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类 Adapter 将不能胜任工作。 使得 Adapter 可以重定义 Adaptee 的部分行为,因为 Adapter 是 Adaptee 的一个子类。 仅仅引入了一个对象,并不需要额外的指针以间接得到 Adaptee。对象适配器则 允许一个 Adapter 与多个 Adaptee即 Adaptee 本身以及它的所有子类(如果有子类的话)
8、同时工作。Adapter 也可以一次给所有的 Adaptee 添加功能。 使得重定义 Adaptee 的行为比较困难。这就需要生成 Adaptee 的子类并且使得 Adapter引用这个子类而不是引用 Adaptee 本身。 使用 Adapter 模式时需要考虑的其他一些因素有:1) Adapter 的匹配程度 对 Adaptee 的接口与 Target 的接口进行匹配的工作量各个Adapter 可能不一样。工作范围可能是,从简单的接口转换(例如改变操作名 )到支持完全不同的操作集合。Adapter 的工作量取决于 Target 接口与 Adaptee 接口的相似程度2) 可插入的 Adapt
9、er 当其他的类使用一个类时,如果所需的假定条件越少,这个类就更具可复用性。如果将接口匹配构建为一个类,就不需要假定对其他的类可见的是一个相同的接口。也就是说,接口匹配使得我们可以将自己的类加入到一些现有的系统中去,而这些系统对这个类的接口可能会有所不同。 3) 使用双向适配器提供透明操作 使用适配器的一个潜在问题是,它们不对所有的客户都透明。被适配的对象不再兼容 Adaptee 的接口,因此并不是所有 Adaptee 对象可以被使用的地方它都可以被使用。双向适配器提供了这样的透明性。在两个不同的客户需要用不同的方式查看同一个对象时,双向适配器尤其有用。 9. 实现类 适 配 器 使 用 的
10、是 继 承让 我 们 看 看 当 API 改 变 时 , 如 何 保 护 应 用 程 序 不 受 影 响 。php view plaincopyprint?1. hello(); 41. $Target-world(); 42. 43. 44. 45. 46. Client:main(); 47. ? 我们 Target 已经明确指出 hello()方法会在未来的版本中改进,甚至不被支持或者淘汰。接下来,现在假设第二版的 Target 已经发布。一个全新的 greet()方法代替了 hello()。 php view plaincopyprint?1. ; 34. 35. 36. /* 37.
11、 * 加入新的方法 38. */ 39. public function greet() 40. echo Greet ; 41. 42. 43. 44. /* 45. * 类适配器角色 46. */ 47. class Adapter extends Adaptee implements Target 48. 49. /* 50. * 源类中没有 world 方法,在此补充 51. */ 52. public function hello() 53. parent:greet(); 54. 55. 56. 57. /* 58. * 客户端程序 59. * 60. */ 61. class C
12、lient 62. 63. /* 64. * Main program. 65. */ 66. public static function main() 67. $adapter = new Adapter(); 68. $adapter-hello(); 69. $adapter-world(); 70. 71. 72. Client:main(); 73. ? 对 象 适 配 器 使 用 的 是 委 派php view plaincopyprint?1. ; 34. 35. 36. /* 37. * 加入新的方法 38. */ 39. public function greet() 40. echo Greet ; 41.