laravel 依賴注入 接口設計

 

假設我如今須要作一個支付服務,那麼我先設計一個接口html

interface PayInterface{
    public function pay(Order $order) : string;
}

而後實現這個接口數據庫

class WeiXinPay implements PayInterface{
    public function pay(Order $order) :string{
        //具體實現
    }
}

開始發現一個問題
微信支付是須要三個關鍵參數的 (appID , appSecret , key)
我就接着修改代碼,我但願這三個參數是經過外部注入的,而不是寫死在WeiXinPay裏面,因而乎我就修改了WeiXinPay的構造函數,並新增了一個接口與實現json

interface PaySettingsInterface {
    public function getSettings() : array;
}

class WeixinPaySettings implements PaySettingsInterface{
    public function getSettings() : array{
        return [
            'app_id' => 'xxxx',
            'app_secret' => 'yyyyy',
            'key' => 'zzzz'
        ];
    }
}

class WeiXinPay implements PayInterface{

    protected $settings;
    public __construct(PaySettingsInterface $settings){
        $this->settings = $settings->getSettings();
    }

    public function pay(Order $order) :string{
        //具體實現
    }
}

好了。感受到這裏這個PayInterface的實現應該是沒問題了。我開始寫服務提供者緩存

$this->app->bind(PaySettingsInterface::class,WeiXinPaySettings::class);
    $this->app->bind(PayInterface::class , WeiXinPay::class);

寫一段測試代碼來跑跑看微信

public function testPay(){
        $orderSn = Strings::randomString('NUMBER+',12);
        $order = factory(Order::class)->make([
            'sn' => $orderSn,
            'uid' => 111,
            'to_uid' => 109,
            'relation_user' => json_encode([109,108,107,104,102,12]),
            'amount' => 1,
            'attach' => 1,
            'status' => Constans::ORDER_NO_PAY,
            'is_settle' => Constans::NO_SETTLE,
        ]);
        
        /**
         * @var $service PayInterface
        */
        $service = $this->app->make(PayInterface::class);
        
        $res = $service->pay($order);
        $this->assertIsString($res);
    }

沒有問題,一切都如預期同樣。(將來我也能夠很容置的將微信支付換成支付寶,只須要在服務提供者切換實現便可)app

過了兩天,又有一個新的需求了。終極問題來了,老闆但願每一次支付的時候收款人都不同,也就是說微信支付的appId,appSecret,appKey每次都不同dom

我開始修改個人代碼,我想着:我讓這些有變更的參數經過構造函數的方式傳遞進來總能夠吧。函數

interface PaySettingsInterface {
    public function getSettings() : array;
}

class WeixinPaySettings implements PaySettingsInterface{
    protected $appID;
    protected $appKey;
    protected $appSecret;

    public function __construct($appID ,$appKey ,$appSecret){
        $this->appID = $appID;
        $this->appKey = $appKey;
        $this->appSecret = $appSecret;
    }

    public function getSettings() : array{
        return [
            'app_id' => $this->appID,
            'app_secret' => $this->appSecret,
            'key' => $this->appKey
        ];
    }
}

class WeiXinPay implements PayInterface{

    protected $settings;
    public __construct(PaySettingsInterface $settings){
        $this->settings = $settings->getSettings();
    }

    public function pay(Order $order) :string{
        //具體實現
    }
}

//而後我修改個人服務提供者
$this->app->bind(PaySettingsInterface::class,function($app){
    //怎麼new 呢? 老闆的需求是可能每次都不一樣,這些數據又可能來自數據庫,也可能來自緩存。
    $instance = new WeixinPaySettings(???);
    return $instance;
});
$this->app->bind(PayInterface::class , WeiXinPay::class);
//到這裏,看來是沒法經過容器自動注入PaySettingsInterface的實現了。那麼我就只能這樣了。在測試代碼中:

public function testPay(){
    $orderSn = Strings::randomString('NUMBER+',12);
    $order = factory(Order::class)->make([
        'sn' => $orderSn,
        'uid' => 111,
        'to_uid' => 109,
        'relation_user' => json_encode([109,108,107,104,102,12]),
        'amount' => 1,
        'attach' => 1,
        'status' => Constans::ORDER_NO_PAY,
        'is_settle' => Constans::NO_SETTLE,
    ]);
    
    //查詢數據庫獲得settings
    $settings = Db::get();
    $paySettings  = new WeixinPaySettings($settings['app_id'],$settings['app_secret'],$settings['app_key']);
    
    $payService = new WeixinPay($paySettings);
    
    $res = $payService->pay($order);
    $this->assertIsString($res);
}

這樣看起來也能夠,可是我困擾了測試

  1. 我沒有辦法簡單的替換支付方式了 (WeixinPay 替換成 AliPay)
  2. 調用方手動的去new 相關的實現,產生了嚴重的依賴。

我但願可以:微信支付

  1. 可以簡單的替換支付方式(服務提供者)
  2. 調用方只須要 調用 pay(Order $order) 方法便可完成支付,即便我切換支付對於調用方來講都是不須要改變的,符合裏氏替換規則
相關文章
相關標籤/搜索