適配器模式俗慈,即根據(jù)客戶端需要扮惦,將某個類的接口轉(zhuǎn)換成特定樣式的接口艇搀,以解決類之間的兼容問題尿扯。
如果我們的代碼依賴一些外部的API,或者依賴一些可能會經(jīng)常更改的類焰雕,那么應(yīng)該考慮用適配器模式衷笋。
下面我們以集成支付寶支付功能為例。
1 問題
假設(shè)支付寶支付類的功能如下:
/** * 支付寶支付類 */class Alipay{ ? ?public function sendPayment() ? ?{ ? ? ? ?echo '使用支付寶支付矩屁。'; ? ?} }// 客戶端代碼$alipay = new Alipay(); $alipay->sendPayment();
我們直接實例化Alipay類完成支付功能辟宗,這樣的客戶端代碼可能很多。
一段時間后吝秕,如果支付寶的Alipay類升級泊脐,方法名由sendPayment()變成goPayment()會怎樣?
所有用了sendPayment()的客戶端代碼都要改變烁峭。
如果Alipay類頻繁升級容客,或者客戶端在很多地方使用,這會是極大的工作量约郁。
2 解決
現(xiàn)在我們用適配器模式來解決缩挑。
我們在客戶端和Alipay類之間加一個中間類,也就是適配器類鬓梅,轉(zhuǎn)換原始的Alipay為客戶端需要的形式供置。
為讓客戶端能調(diào)用到統(tǒng)一的類方法,我們先定義一個適配器接口:
/** * 適配器接口绽快,所有的支付適配器都需實現(xiàn)這個接口芥丧。 * 不管第三方支付實現(xiàn)方式如何,對于客戶端來說谎僻,都 * 用pay()方法完成支付 */interface PayAdapter{ ? ?public function pay(); }
因為Alipay類我們無法控制娄柳,而且它有可能經(jīng)常更新,所以我們不對它做任何修改艘绍。
我們新建一個AlipayAdapter適配器類,在pay()中轉(zhuǎn)換Alipay的支付功能秫筏,如下:
/** * 支付寶適配器 */class AlipayAdapter implements PayAdapter{ ? ?public function pay() ? ?{ ? ? ? ?// 實例化Alipay類诱鞠,并用Alipay的方法實現(xiàn)支付 ? ? ? ?$alipay = new Alipay(); ? ? ? ?$alipay->sendPayment(); ? ?} }
客戶端使用方式:
// 客戶端代碼$alipay = new AlipayAdapter();// 用pay()方法實現(xiàn)支付$alipay->pay();
這樣这敬,當(dāng)Alipay的支付方法改變,只需要修改AlipayAdapter類就可以了崔涂。
3?適配新類
有了適配器后,擴展也變得更容易了。
繼續(xù)以上的例子缭保,在支付寶的基礎(chǔ)上,我們再增加微信支付艺骂,它與支付寶的支付方式不同诸老,必須通過掃碼才能支付别伏。
這種情況也應(yīng)該使用適配器,而不是直接使用微信的支付功能忧额。
代碼如下:
/** * 微信支付類 */class WechatPay{ ? ?public function scan() ? ?{ ? ? ? ?echo '掃描二維碼后,'; ? ?} ? ?public function doPay() ? ?{ ? ? ? ?echo '使用微信支付'; ? ?} }/** * 微信支付適配器 */class WechatPayAdapter implements PayAdapter{ ? ?public function pay() ? ?{ ? ? ? ?// 實例化WechatPay類睦番,并用WechatPay的方法實現(xiàn)支付。 ? ? ? ?// 注意抡砂,微信支付的方式和支付寶的支付方式不一樣,但是 ? ? ? ?// 適配之后注益,他們都能用pay()來實現(xiàn)支付功能碴巾。 ? ? ? ?$wechatPay = new WechatPay(); ? ? ? ?$wechatPay->scan(); ? ? ? ?$wechatPay->doPay(); ? ?} }
客戶端使用:
// 客戶端代碼$wechat = new WechatPayAdapter();// 也是用pay()方法實現(xiàn)支付$wechat->pay();
這就是適配器的擴展特性。
我們創(chuàng)建了一個用于處理第三方類(支付寶丑搔、微信支付)的方法厦瓢,
如果它們的API有變化,我們僅需修改客戶端依賴的適配器類就可以啤月,不用修改煮仇、暴露第三方類本身。
4?UML圖
以上適配器模式的代碼對應(yīng)UML如下:
注意:適配器模式中谎仲,適配器類的名稱和創(chuàng)建方式一定是不會頻繁改動的浙垫。
對于客戶端來說,引用適配器類的方式應(yīng)該是統(tǒng)一而不變的郑诺,這才算是正確使用適配器夹姥。
5 總結(jié)
大的應(yīng)用都會不斷地加入新庫和新API。
為避免它們的變更引發(fā)問題辙诞,應(yīng)該用適配器模式包裝起來辙售,提供應(yīng)用統(tǒng)一的引用方式。
它會讓我們的代碼更具結(jié)構(gòu)化飞涂,便于管理和擴展旦部。
參考資料:
Design Patterns: The Adapter PatternAdapter pattern - WikiPediaPractical Aspects of the Adapter PatternAdapter Design Pattern
互聯(lián)網(wǎng)+時代祈搜,時刻要保持學(xué)習(xí),攜手千鋒PHP,Dream It Possible士八。