最近在給公司的商城作第三方支付的對接,看了一下之前的微信支付,感受結合了一下以前看的設計模式,想試試能不能在上面用上。一番研究後,感受也是能夠,就是可能有點牛刀小試。php
一、 場景描述算法
微信支付有預下單、退款、訂單查詢等等接口,看了下第三方支付,一樣也是相似的三板斧。很容易,咱們會想到如下這個類圖:
圖中能夠看到WxPay和SandPay都繼承了Pay,他們有一樣的行爲:下單、退款、訂單查詢,可是,咱們能夠知道他們的實現確定是不同的,這樣咱們都必須覆蓋父類的方法,來處理不同支付的細節,這樣的時候,代碼就會顯得冗餘,每一個子類都須要覆蓋父類的實現,如何讓代碼更加統一呢?
二、 問題分析編程
這個時候,咱們須要的是將行爲獨立出來,讓其封裝在特定的行爲類裏,這樣,咱們就能「指定」行爲到支付的實例。好比說,咱們想要產生一個新的第三方支付實例,咱們能夠動態的讓其實現微信支付的下單操做(固然這是不合理)。
這時,咱們用到了一個很重要的設計原則:設計模式
針對接口編程,而不是針對實現編程
咱們用接口表明每一個行爲,好比說,OrderBehavior和RefundBehavior,行爲的每一個實現都講實現其中一個接口。支付類不會實現下單和退款的接口,而是有其餘類專門實現。咱們稱這種類叫「行爲」類。和之前作法不同的地方在於,之前的作法是:行爲由超類或者子類繼承某個接口,自行實現。這兩種作法都依賴與「實現」,這樣咱們很容易被實現綁死,很難在後來改變行爲(除非寫更多的代碼)。新的設計裏,支付類將使用由接口所表示的行爲,因此實際的「實現」並不會綁死在支付類中,這樣,支付類就不用在瞭解行爲實現的細節。微信
來看一下新的類圖:
以及,看看Pay抽象類中統一後的方法代碼:微信支付
<?php class Pay{ public $orderBehavior; public $refundBehavior; public function orderPay() { return $this->orderBehavior->orderPay(); } public function refund() { return $this->refundBehavior->refund(); } }
而後在實現的時候,子類就能這樣動態實現this
<?php //實例化微信支付 $pay = new WxPay(); //賦值微信下單實例 $pay->orderBehavior = new WxOrder(); //實現了微信支付的下單 $pay->orderPay(); //第三方支付同理 $pay = new SandPay(); $pay->orderBehavior = new SandOrder(); $pay->orderPay();
可能這時候有人會問,我明明能夠直接就在具體類裏實現這個方法,也不用多寫這麼多類與接口,正常來講,支付也不會改變其實現方式。
是的,通常來講,支付是不會修改的,可是若是忽然說如今不用第三方支付,所有都用微信支付,那麼咱們代碼的修改可能會不少了,可是用這種實現咱們只須要改動一個很小地方:spa
<?php //在外部看起來咱們調用的是第三方支付 $pay = new SandPay(); //真實咱們的實現是微信支付 $pay->orderBehavior = new WxOrder(); //咱們因爲不用管實際的細節,也不用作太多的代碼改動,並且由微信支付原本的穩定實現,咱們也能很放心的說,代碼不會出現bug $pay->orderPay();
這一種實現方式,也符合了一個設計原則:設計
多用組合,少用繼承
這裏支付對下單甚至退款的操做,實際的實現都不是經過繼承獲得的,而是經過將其餘類的結合,不只能夠將算法族封裝成類,更能夠「在運行時動態改變行爲」,只要行爲對象符合正確的接口標準便可。code
這個狀況,你以爲是用了哪一種設計模式?
一、 場景描述
在處理完多種支付的場景後,咱們在開始加入了第三方支付的代碼,在寫的過程發現,下單操做和微信支付相似,咱們須要在請求 以前,組裝請求必要參數、簽名,獲得返回結果後,對數據驗籤,而後進行本身的業務邏輯,最後返回一個應答。 同理,退款操做也是同樣:組裝請求必要參數、簽名等等,和下單操做幾乎處理邏輯的順序或者說結構很類似,若是咱們繼續這麼 編寫,會發現不少重複代碼,這個時候要怎麼處理呢?
二、 問題分析
在解決問題以前,先說一下一個重要的設計原則:
找出應用中可能須要變化之處,把它們獨立出來,不要和那些不須要變化的代碼混在一塊兒。
通俗來講,就是要善於發現現實中的變與不變,抽離不變的地方,使其能複用,而後讓變化的部分自行解決處理。 在這個場景中,很特殊的,咱們發現不論是微信的下單、退款,仍是第三方的下單、退款,他們業務的邏輯幾乎是一個流水線上出 來的,就是他們不變的地方,而變化的是什麼呢?很明顯就是具體的實現業務不一樣,這部分應該由業務本身實現。 這樣,咱們將該描述用類圖表示以下:
而代碼具體以下:
<?php abstract class PayAction{ public function doAction() { $this->generateRequestData(); $this->generateSign(); $this->request(); $this->verifiedSign(); $this->handleResponse(); $this->returnMsg(); } abstract protected function generateRequestData(); abstract protected function generateSign(); abstract protected function request(); abstract protected function handleResponse(); abstract protected function verifiedSign(); abstract protected function returnMsg(); } class WxOrder extends PayAction{ public function orderPay() { $this->doAction(); } }
只針對這一系列操做,咱們能夠寫成這個樣子,這樣全部的行爲都同樣了,只須要實現自身不同的地方便可。
一、若是把第一種狀況的結構和第二種狀況的結構,結合起來,最後是什麼樣子呢,咱們能夠看一下:
這樣,整個結構就很清晰了,客戶端調用實現了行爲的下單或者退款類,不須要知道具體的細節,而且能夠動態的改變行爲的對象;而行爲的操做的重複部分又被抽出來,只須要各自實現變化的部分,不變的部分都由抽象類PayAction先定義好,再由不同的支付類實現公共的部分:簽名與驗籤,甚至最後固定的返回正確和錯誤時的格式都是同一個支付裏,相同的部分;最後不同的只有不一樣請求時候,不同的請求參數,以及得到返回參數後的不同的處理了。
類和接口的確比一開始的設計要多了很多,可是結構很是清晰,並且修改的時候,能夠頗有針對性的修改,對於客戶端來講,調用是透明的,對於提供服務的咱們來講,不論是新增一個支付方式,仍是多了一個支付操做,咱們均可以很好的增長代碼,不用修改現有代碼,而這個也是一個常常說到的設計原則:
開閉原則:對修改關閉,對擴展開放
第一和第二個場景,它們分別對應的設計模式是什麼?
第一種設計模式是:策略模式,而第二種是:模板方法模式
第一種咱們能夠清楚的看到,咱們對於如何運行一個行爲的時候,咱們是將其封裝在一個類中處理,而且,它們能夠相互替換。算法的改變由客戶來決定,能夠動態的改變。第二種咱們能夠看到的是,整個算法的結構已經被定義好,跟着預約好的模板來編寫咱們的算法,就能夠實現類似的功能有條不紊的編寫下去,不會出現多餘的部分,而且能夠專心的處理自身特別的業務邏輯。因此,之後若是代碼的相關結構很類似,能夠選用模板方法模式來編寫;若是業務中的某些行爲能夠被抽象而且有須要動態改變的時候,能夠考慮策略模式來編寫。