假設我現在需要做一個支付服務,那么我先設計一個接口
interface PayInterface{
public function pay(Order $order) : string;
}
然后實現這個接口
class WeiXinPay implements PayInterface{
public function pay(Order $order) :string{
//具體實現
}
}
開始發現一個問題
微信支付是需要三個關鍵參數的 (appID , appSecret , key)
我就接着修改代碼,我希望這三個參數是通過外部注入的,而不是寫死在WeiXinPay里面,於是乎我就修改了WeiXinPay的構造函數,並新增了一個接口與實現
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);
}
沒有問題,一切都如預期一樣。(未來我也可以很容置的將微信支付換成支付寶,只需要在服務提供者切換實現即可)
過了兩天,又有一個新的需求了。終極問題來了,老板希望每一次支付的時候收款人都不一樣,也就是說微信支付的appId,appSecret,appKey每次都不一樣
我開始修改我的代碼,我想着:我讓這些有變動的參數通過構造函數的方式傳遞進來總可以吧。
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);
}
這樣看起來也可以,但是我困擾了
- 我沒有辦法簡單的替換支付方式了 (WeixinPay 替換成 AliPay)
- 調用方手動的去new 相關的實現,產生了嚴重的依賴。
我希望能夠:
- 能夠簡單的替換支付方式(服務提供者)
- 調用方只需要 調用 pay(Order $order) 方法即可完成支付,即使我切換支付對於調用方來說都是不需要改變的,符合里氏替換規則