时间:2021-07-01 10:21:17 帮助过:24人阅读
适配器模式只是将某个对象的接口适配为另一个对象所期望的接口。
适配器模式应用问题与解决方案
在应用程序中,您也许会使用一个在体系结构上可靠稳定的工作代码库。不过我们常常会添加新的功能,这些功能要求采用不同的方式使用现有的对象,而不是采用原先设计的方式。此时,障碍可能只是新功能需要一个不同的名字。在较为复杂的场景中,障碍也可能是新功能需要与原始对象稍有不同的行为。
针对上述问题,我们采用的解决方案是使用适配器模式构建另一个对象。这个Adapter对象充当了原始应用与新功能之间的中介。适配器模式为已有的对象定义了新的接口,从而能够匹配新对象的要求。
问题
假设支付宝支付类的功能如下:
/** * 支付宝支付类 */ class Alipay { public function sendPayment() { echo '使用支付宝支付。'; } } // 客户端代码 $alipay = new Alipay(); $alipay->sendPayment();
我们直接实例化Alipay类完成支付功能,这样的客户端代码可能很多。
一段时间后,如果支付宝的Alipay类升级,方法名由sendPayment()变成goPayment()会怎样?
所有用了sendPayment()的客户端代码都要改变。
如果Alipay类频繁升级,或者客户端在很多地方使用,这会是极大的工作量。
解决
现在我们用适配器模式来解决。
我们在客户端和Alipay类之间加一个中间类,也就是适配器类,转换原始的Alipay为客户端需要的形式。
为让客户端能调用到统一的类方法,我们先定义一个适配器接口:
/** * 适配器接口,所有的支付适配器都需实现这个接口。 * 不管第三方支付实现方式如何,对于客户端来说,都 * 用pay()方法完成支付 */ interface PayAdapter { public function pay(); }
因为Alipay类我们无法控制,而且它有可能经常更新,所以我们不对它做任何修改。
我们新建一个AlipayAdapter适配器类,在pay()中转换Alipay的支付功能,如下:
/** * 支付宝适配器 */ class AlipayAdapter implements PayAdapter { public function pay() { // 实例化Alipay类,并用Alipay的方法实现支付 $alipay = new Alipay(); $alipay->sendPayment(); } }
客户端使用方式:
// 客户端代码 $alipay = new AlipayAdapter(); // 用pay()方法实现支付 $alipay->pay();
这样,当Alipay的支付方法改变,只需要修改AlipayAdapter类就可以了。
适配新类
有了适配器后,扩展也变得更容易了。
继续以上的例子,在支付宝的基础上,我们再增加微信支付,它与支付宝的支付方式不同,必须通过扫码才能支付。
这种情况也应该使用适配器,而不是直接使用微信的支付功能。
代码如下:
/** * 微信支付类 */ class WechatPay { public function scan() { echo '扫描二维码后,'; } public function doPay() { echo '使用微信支付'; } } /** * 微信支付适配器 */ class WechatPayAdapter implements PayAdapter { public function pay() { // 实例化WechatPay类,并用WechatPay的方法实现支付。 // 注意,微信支付的方式和支付宝的支付方式不一样,但是 // 适配之后,他们都能用pay()来实现支付功能。 $wechatPay = new WechatPay(); $wechatPay->scan(); $wechatPay->doPay(); } }
客户端使用:
// 客户端代码 $wechat = new WechatPayAdapter(); // 也是用pay()方法实现支付 $wechat->pay();
这就是适配器的扩展特性。
我们创建了一个用于处理第三方类(支付宝、微信支付)的方法,
如果它们的API有变化,我们仅需修改客户端依赖的适配器类就可以,不用修改、暴露第三方类本身。
UML图
以上适配器模式的代码对应UML如下:
总结
大的应用都会不断地加入新库和新API。
为避免它们的变更引发问题,应该用适配器模式包装起来,提供应用统一的引用方式。
它会让我们的代码更具结构化,便于管理和扩展。
以上就是面向对象进阶-设计模式:适配器模式的详细内容,更多请关注Gxl网其它相关文章!