Android 設計模式實戰之關於封裝計費代碼庫的策略模式詳談

寫在以前算法

這周生活上出現了不少的不如意,從週一開始就以爲哪裏出現了問題,而後就是各類煩躁的情緒,後來事情還真是如預感的那樣發生了,非常心痛,但也迫不得已,但願你們都好好珍惜本身身邊的人:友人,親人,家人,願一切和氣。編程

正文json

一直以來都飽受公司APP客戶端關於各類計費點的折磨。一段時間內,同一應用或不一樣應用間接入多家的計費模式,而後須要在不一樣的計費間來回的跳轉,大大的增長了出錯的概率,甚至有時候出現計費點錯亂的現象,這就致使了工做效率的下降,並且作了大量的無用功,費時費力尚未成果。安全

基於這種困擾,一直以來都想封裝一套統一實現計費策略的代碼庫,可是一直沒有着手去作。最近有一套APP要實現微信,支付寶支付等計費,那麼正好能夠利用這個機會來實現一整套的計費點代碼庫。微信

而本篇博文的產生就是計費代碼庫的一個縮影,主要講解的是如何使用策略模式設計這種可使用多種算法來完成一樣的目標。網絡

需求分析

下面來簡單的看下需求:學習

本款APP要求能實現微信、支付寶等支付方式進行購買虛擬幣,購買完成後需上傳購買數量,同時要更新用戶所擁有的虛擬幣。微信支付

ok,咱們來分析下需求,主要能夠分三步:url

首先,購買虛擬幣,這是一個動做,在這個動做中有能夠分爲多種形式,好比這裏的用微信和支付寶購買;設計

其次,購買完成後,要上傳服務端存儲用戶購買的虛擬幣數量;

最後是更新UI,顯示用戶目前擁有的虛擬幣。

由上面的分析,咱們對需求有了很清晰的認識,這裏的主要任務是設計一個簡版的計費代碼庫,因此咱們主要關心的也就是計費這一模塊,其餘的部分咱們並不能考慮到計費庫裏,我相信你們都能明白,一個庫,幹好一件事便可。

說到計費,咱們的需求中也就是購買虛擬幣,而購買虛擬幣這個行爲能夠是多種形式的,好比說使用微信,支付寶等,那麼各類支付方式是否是都是同樣的實現呢?

顯然不是,它們都有本身的實現方式,並且每一家也都有本身的計費流程等,因此咱們的庫設計應該是能夠符合多種實現的計費模式,而且能很好的實現擴展,並且各類實現能夠互相替換,好比,購買虛擬幣能使用微信支付也能使用支付寶支付,這就實現了互相替換。那麼有沒有這麼一種實現方式呢?

顯然答案是有的,這就是咱們要講的策略模式。

模式定義

那到底什麼是策略模式呢?

策略模式是定義了一族算法,並分別將每組算法封裝起來,使它們之間能夠互相替換。算法的變化獨立於使用算法的用戶。

它主要使用在哪些場景呢?

①:同一種行爲,多種實現。

②:安全地封裝多種同一類型的操做。

③:同一抽象類有多個子類,而又須要選擇具體子類操做。

由它的定義和使用場景,咱們更加確信了咱們的計費代碼庫使用這種模式是行得通的。

那麼,咱們到底該怎麼使用策略模式進行設計咱們的庫呢?

詳細設計

說道關於計費的問題,相信咱們每一個人都很清楚的瞭解,那就是須要有一個相似pay的方法專門用來進行支付,因此咱們得有一個支付類,而且這個支付類帶有一個pay的支付方法,以下設計:

這裏我並無針對參數來進行詳細的構建,畢竟只是一個針對簡單的demo級別庫,主要仍是用來學習策略模式的,因此,我這裏就只是簡單的設計一個String類型的url參數,並讓他返回一個String類型的結果,由於大多數網絡請求都是返回的一個json串,便於解析。

能夠看到我在這裏設計了一個基類BasePay,並帶有一個pay方法,接過相信支付功能的同窗都應該知道,爲了保證支付的安全性,不少支付渠道都會有一個預訂單的接口,在預訂單中返回一些重要的信息,並在支付接口中調用,例如微信支付就是這樣模式。

因此爲了知足這種需求,咱們就須要有這麼一個預訂單的方法,那麼咱們在咱們的基類中添加上這種方法:

一樣的沒有作過多的參數考慮,只是讓他傳進一個String類型的URL地址。

ok,咱們的基類已經設計出來了,那麼各類各類的支付均可以繼承此類來完成預訂單和支付的能力,那麼你以爲這樣的設計怎麼樣呢?

假若有一種支付是不須要預訂單呢?

這樣也很簡單,我就直接作一個空的實現。

沒錯,這種能夠實現,那接入有100個支付,其中有80個沒有預訂單的實現呢?

難道你要作上80個空實現嗎?

顯然這是不可取的,那麼咱們該怎麼作呢?

設計原則:保持共性,封裝變化

假若有80個支付渠道不須要預訂單,20個是須要預訂單的,由此能夠看出這個預訂單的接口是變化的,並且剩下的20個須要預約單的每一個實現也是不相同的,那麼咱們根據面向對象的設計原則:把須要變化的部分給獨立的封裝起來,只保留相同的部分,那麼咱們是否是能夠把它給獨立的封裝起來以便應付隨時變化的實現呢?

一樣的道理支付接口的實現也是不盡相同的,是否也應該封裝起來呢?

由此想到了另外的一個設計原則。

設計原則:針對接口編程,而不是針對實現編程

針對接口編程,不針對具體的實現進行編程,這樣咱們就能夠區分不一樣的實現獨立的完成,同時每一個支付的業務只面對接口,不用關心具體的實現是怎麼完成的:

由上圖所示,分別的定義了兩個接口PreOrderInterface和PayInterface,並分別包含一個方法,而且咱們分別對這兩個接口作了兩個實現,分別是針對微信和支付寶的。

這樣一來的話,咱們就把原來的BasePay裏面的兩個變化的方法分別的抽取出來封裝成接口,並根據不一樣的業務渠道進行實現。

那咱們是否是能夠直接用BasePay基類來實現這個兩個接口呢?

若是直接用BasePay來實現接口,那和不抽取變化封裝有什麼區別呢,因此咱們不能直接用BasePay來或者是它的子類來直接的實現這兩個接口,那麼到底該怎麼運用這兩個接口呢?

由此咱們想到了另外的一種設計原則:

設計原則:少用繼承,多用組合

可使用組合的形式來添加對接口的運用,有了接口的引用,不只能夠包含了算法的封裝,同時也能夠在運行時動態的改變行爲。

這樣咱們就有了對這兩個接口的引用,那麼咱們在定義兩個對接口調用的方法:

同時爲了便於擴展,爲了能在運行時改變咱們的行爲方式,咱們能夠用設置接口的方式來實現:

ok,這樣將會更加利於咱們的擴展。下面來看看完整的BasePay類吧:

這樣的話,當咱們須要預訂單的時候直接的調用performPreOrder方法,在支付的時候調用performPay方法,能夠徹底的不用考慮它的直接實現究竟是怎麼作的,咱們只須要關心的就是調用的接口而已,從而下降了耦合性。

固然這只是咱們的基類,咱們還須要它的具體實現類:

若是具體類有相同的功能,那麼能夠在父類中定義一個抽象方法,同時改變父類爲抽象類,那麼這個相同的功能就能夠直接的在子類中實現,好比說要打印一個輸出支付結果的log日誌,那麼就能夠分別在子類中完成。

ok,有了子類具體實現,那麼到底該怎麼使用呢:

其實也很簡單,既然父類已經有了對接口的引用,那麼咱們能夠在子類中對接口實例化:

這裏能夠看出,當咱們須要什麼的時候就進行什麼初始化,若是不須要,能夠直接的不用管它。

那麼在來看看咱們是怎麼使用的:

當咱們肯定要使用哪一種支付方式時,能夠利用面向對象多態的特性指定具體的子類,而後在子類中實例化父類對接口的具體引用實現,當使用支付接口時,能夠直接的調用父類提供的支付方法完成支付。

父類只須要關心引用的接口,而不須要關心具體的接口實現,實現解耦;

子類只須要關係對父類引用接口的實例化,和所共有的特性實現,也不須要具體的實現;

對具體的實現進行了封裝,每種具體的實現類都實現接口,它只關注具體的實現,這樣就把整個變化封裝在特有的實現類中,經過接口獲取到對它的引用,那麼整個結構就顯得很是的清晰,各個模塊都只須要關注本身的領域,並且模塊間耦合性也降到最低。

把變化進行封裝,保持共性,經過接口制定具體的算法,這就是咱們的策略模式。

最後在來看下整個類圖:

ok,這樣咱們就利用策略模式實現了一個簡單的計費代碼庫,每當有新的計費加入時,咱們只須要添加具體的實現類和具體的支付類,並不須要修改原來的代碼,而且能夠多方面複用。

到這裏基本的策略模式就講完了,計費代碼庫demo級也就出來了,固然它並不完善,只是關注策略模式的這一部分,其餘的像網絡請求,一些幫助類等都是須要封裝的,也不是這個主題所關注的,因此這就須要你們根據本身的需求來實現了。

好了,今天就講到這裏吧。

各位若是還有哪裏不明白的,或是我這裏講的還不夠透徹,亦或是講錯了的地方請留言指正,讓咱們共同進步,謝謝

同時,請你們掃一掃關注個人微信公衆號,雖然寫的不是很勤,可是每一篇都有質量保證,讓您學習到真正的知識。

相關文章
相關標籤/搜索