Adapter(适配器模式)
---- 加个“适配器”以便于复用
将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
应用场景
如果我们的代码依赖一些外部的API,或者依赖一些可能会经常更改的类,那么应该考虑用适配器模式。
你想使用一个已经存在的类,而它的接口不符合你的需求
你想创建一个可以复用的类,该类可以与其他不相关的类或者不可预见的类(即那些接口可能不一定兼容的类)协同工作。
(仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
在设计模式中,适配器模式有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
Adapter适配器模式包含两种:
类适配器模式(使用继承的适配器)
对象适配器模式(使用委托的适配器)
类适配器
是通过类的继承实现的适配,而对象适配器
是通过对象间的关联关系,组合关系实现的适配。二者在实际项目中都会经常用到,由于对象适配器是通过类间的关联关系进行耦合的,因此在设计时就可以做到比较灵活,而类适配器就只能通过覆写源角色的方法进行拓展,在实际项目中,对象适配器使用到的场景相对较多。
模式参与者
Target 目标角色 该角色定义把其他类转换为何种接口,也就是我们的期望接口。
Adaptee 源角色 你想把“谁”转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象。
Adapter 适配器角色 适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,他的职责非常简单:把源角色转换为目标角色。
类图结构
类适配器使用多重继承对一个接口与另一个接口进行匹配,如下图所示。
对象匹配器依赖于对象组合,如下图所示。
代码示例
<?php /** * 第一种方式:类适配器 */ interface Adaptee {// 源角色 public function sampleMethod1(); } class Target{ public function sampleMethod2() {echo '--------';} } class Adapter extends Target implements Adaptee { public function sampleMethod1() {echo '++++++++';} } $adapter = new Adapter(); $adapter->sampleMethod1();//输出:++++++++ $adapter->sampleMethod2();//输出:-------- /** * 第二种方式:对象适配器 */ class Adaptee { // 源角色 public function sampleMethod1() { echo '++++++++'; } } class Target { public function sampleMethod2() { echo '--------';} } class Adapter extends Target { private $_adaptee; public function __construct(Adaptee $adaptee) { $this->_adaptee = $adaptee; } public function sampleMethod1() { $this->_adaptee->sampleMethod1(); } } $adapter = new Adapter(new Adaptee()); $adapter->sampleMethod1();//输出:++++++++ $adapter->sampleMethod2();//输出:-------- ?>
适配器模式优点
适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就成。
增加了类的透明性。我们访问的是目标角色,但是实现却在源角色里。
提高了类的复用度。源角色在原有系统中还是可以正常使用的。
灵活性非常好。不想要适配器时,删掉这个适配器就好了,其他代码不用改。
拓展思路
什么时候使用Adapter模式?
很多时候,我们并非是从零开始编程,经常会用到现有的类。让现有的类适配新的接口API时,使用Adapter模式似乎是理所当然的。不过实际上,我们在让现有的类适配新的接口时,常常会有“只要将这里稍微修改下就可以了”的想法,一不留神就会修改现有的代码。但是需要注意的是,如果要对已经测试完毕的现有代码进行修改,就必须在修改后重新进行测试。
使用Adapter模式就可以在完全不改变现有代码的前提下适配新的接口API。此外,在Adapter模式中,并非一定需要具体的代码。只要知道现有类的功能,就可以编写出新的类。
版本升级与兼容性
软件的生命周期总是伴随着版本的升级,而在版本升级的时候经常会出现“与旧版本的兼容性”问题。如果能够完全抛弃旧版本,那么维护起来就会轻松很多,但现实中往往无法这样做。这样,就可以使用Adapter模式使新旧版本兼容,帮助我们轻松地同时维护新旧版本。
下面是 提高与与旧版本兼容性的Adapter模式的示意图
- 功能完全不同的类 当然,当Adaptee 和 Target 的功能完全不同时,Adapter模式是无法使用的,就如同我们无法用交流100伏特电压让自来水管出水一样。
相关模式
Bridge 模式
Bridge 模式的结构和对象适配器类似,但是Bridge模式的出发点不同:Bridge 目的是将接口部分和实现部分分离,从而对它们可以较为容易也相对独立的加以改变。而 Adapter 则意味着改变一个已有对象的接口。
Decorator 模式
Decorator 模式增强了其他对象的功能而同时又不改变它的接口,Adpater 模式则是填补不同接口(API)之间的缝隙。
Proxy 模式
Proxy 模式在不改变它的接口的条件下,为另一个对象定义了一个代理。