適配器模式

適配器模式,即根據客戶端需要,將某個類的接口轉換成特定樣式的接口,以解決類之間的兼容問題。

如果我們的代碼依賴一些外部的API,或者依賴一些可能會經常更改的類,那麼應該考慮用適配器模式。

下面我們以集成支付寶支付功能爲例。

1 問題

假設支付寶支付類的功能如下:

/**
 * 支付寶支付類
 */
class Alipay
{
    public function sendPayment()
    {
        echo '使用支付寶支付。';
    }
}

// 客戶端代碼
$alipay = new Alipay();
$alipay->sendPayment();

我們直接實例化Alipay類完成支付功能,這樣的客戶端代碼可能很多。

一段時間後,如果支付寶的Alipay類升級,方法名由sendPayment()變成goPayment()會怎樣?

所有用了sendPayment()的客戶端代碼都要改變。

如果Alipay類頻繁升級,或者客戶端在很多地方使用,這會是極大的工作量。

2 解決

現在我們用適配器模式來解決。

我們在客戶端和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類就可以了。

3 適配新類

有了適配器後,擴展也變得更容易了。

繼續以上的例子,在支付寶的基礎上,我們再增加微信支付,它與支付寶的支付方式不同,必須通過掃碼才能支付。

這種情況也應該使用適配器,而不是直接使用微信的支付功能。

代碼如下:

/**
 * 微信支付類
 */
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有變化,我們僅需修改客戶端依賴的適配器類就可以,不用修改、暴露第三方類本身。

4 UML圖

以上適配器模式的代碼對應UML如下:

適配器模式UML

注意:適配器模式中,適配器類的名稱和創建方式一定是不會頻繁改動的。

對於客戶端來說,引用適配器類的方式應該是統一而不變的,這纔算是正確使用適配器。

5 總結

大的應用都會不斷地加入新庫和新API。

爲避免它們的變更引發問題,應該用適配器模式包裝起來,提供應用統一的引用方式。

它會讓我們的代碼更具結構化,便於管理和擴展。

 

參考資料:

  1. Design Patterns: The Adapter Pattern
  2. Adapter pattern - WikiPedia
  3. Practical Aspects of the Adapter Pattern
  4. Adapter Design Pattern
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章