大家可能對銀行的交易系統(tǒng)充滿敬畏之情,一聽說是銀行的IT人員,立馬想當(dāng)然地認(rèn)為這是個很厲害的人物,那我們今天就來對銀行的交易系統(tǒng)做一個初步探討。國內(nèi)一家大型集團(tuán)(全球500強(qiáng)之一)計劃建立全國一卡通計劃,每個員工配備一張IC卡,該卡基本上就是萬能的,門禁系統(tǒng)用它,辦公系統(tǒng)使用它作為認(rèn)證,你想打開自己的郵箱,沒有它就甭想了,它還可以用來進(jìn)行消費(fèi),比如到食堂吃飯,到園區(qū)內(nèi)的商店消費(fèi)等等,甚至洗澡、理發(fā)、借書、買書等等都可以用它,只要這張卡內(nèi)有余額,在集團(tuán)內(nèi)部就是一張借記卡(當(dāng)然還有一些內(nèi)部的補(bǔ)助通過該卡發(fā)放,合理避稅嘛)。我們要講解的就是一卡通項目聯(lián)機(jī)交易子系統(tǒng), 類似于銀行的交易系統(tǒng),可以說它是交易系統(tǒng)的mini版吧。
該項目還是具有一定的挑戰(zhàn)性,集團(tuán)公司的架構(gòu)分為三層:總部、省級分部、市級機(jī)構(gòu),業(yè)務(wù)要求是一卡通推廣到全國,一名員工從北京出差到了上海,憑一卡通他能在北京做的事情在上海同樣能完成。對于聯(lián)機(jī)交易子項目,異地分支機(jī)構(gòu)與總部之間的通信采用了MQ(Message Queue,消息隊列)傳遞消息,也就是我們觀察者模式的BOSS版,與目前的通過POS機(jī)刷信用卡基本上是一個道理。
聯(lián)機(jī)交易子系統(tǒng)有一個非常重要的子模塊(Module)——扣款子模塊。這個模塊太重要了!從業(yè)務(wù)上來說,扣款失敗就代表著所有的商業(yè)交易關(guān)閉,這是不允許發(fā)生的;從技術(shù)上來說,扣款的異常處理、事務(wù)處理、魯棒性都是不容忽視的,特別是飯點(diǎn)時間,并發(fā)量是很恐怖的,這對我們架構(gòu)師提出了很高的要求。
我們詳細(xì)分析一下扣款子模塊,每個員工都有一張IC卡,他的IC卡上有以下兩種金額。
1.固定金額
固定金額是指員工不能提現(xiàn)的金額,即使你的固定金額有10萬元,你也只能干瞪眼看著,不能提取現(xiàn)金,那這部分金額有來做什么呢?只能用來特定消費(fèi),什么特定消費(fèi)?員工日常必需的消費(fèi),例如食堂內(nèi)吃飯、理發(fā)、健身等活動。
2.自由金額
自由金額是可以提現(xiàn)的,當(dāng)然也可以用于消費(fèi)。每個月初,總部都會為每個員工的IC卡中打入固定數(shù)量的金額,然后提倡大家在集團(tuán)內(nèi)的商店消費(fèi)。
在實際的系統(tǒng)開發(fā)中,架構(gòu)設(shè)計的是一張IC卡綁定兩個賬戶:固定賬戶和自由賬號,本書為了簡化描述還是使用固定金額和自由金額。既然有消費(fèi),系統(tǒng)肯定要扣款處理,系統(tǒng)內(nèi)有兩套扣款規(guī)則。
1.扣款策略一
該類型的扣款會對IC卡上的兩個金額都會產(chǎn)生影響,計算公式如下:
IC卡固定余額 = IC卡現(xiàn)有固定余額 – 交易金額 / 2
IC卡自由余額 = IC卡現(xiàn)有自由金額 - 交易金額 / 2
也就是說,該類型的的消費(fèi)分別在固定金額和自由金額上各扣除一半。它適用于什么消費(fèi)場景呢?固定消費(fèi)場景,例如吃飯、理發(fā)等情況下的扣款,為什么要這么做呢?為了防止亂請客的情況,你請別人吃飯,好,你自己也要出一半,你樂意呀。
2.扣款策略二
全部從自由金額的上扣除,由于集團(tuán)內(nèi)的各種消費(fèi)、服務(wù)非常齊全,而且比市面價格稍低,員工還是很樂意到這里消費(fèi)的,而且很多員工本身就住在集團(tuán)附近,基本上就是公司即家,家即公司。
今天要講的重點(diǎn)就是這兩種消費(fèi)的扣款策略,該怎么設(shè)計?先想清楚了再回答,你要知道這種聯(lián)機(jī)交易,日后允許你大規(guī)模變更的可能性基本上是零,所以系統(tǒng)設(shè)計的時候要做到可拆卸(Pluggable)的架構(gòu),避免日后維護(hù)的大量開支。
很明顯,這是一個策略模式的實際應(yīng)用,但是你還記得策略模式是有缺陷的嗎?它的具體策略必須暴露出去,而且還要由上層模塊初始化,這不合適,與迪米特原則有沖突,高層次模塊對低層次的模塊應(yīng)該僅僅處在接觸的層次上,而不應(yīng)該是耦合的關(guān)系,否則,維護(hù)的工作量就會非常大。問題提出了,那我們就應(yīng)該想辦法來修改這個缺陷,正好工廠方法模式可以幫我們產(chǎn)生指定的對象,但是問題又來了,工廠方法模式要指定一個類,它才能產(chǎn)生對象,怎么辦?引入一個配置文件進(jìn)行映射,避免系統(tǒng)僵化情況的發(fā)生,我們以枚舉類完成該任務(wù)。
還有一個問題,一個交易的扣款模式是固定的,根據(jù)其交易編號而定,那我們怎么把交易編號與扣款策略對應(yīng)起來呢?采用狀態(tài)模式或責(zé)任鏈模式都可以,如果采用狀態(tài)則認(rèn)為交易編號就是一個交易對象的狀態(tài),對于一筆確定的交易(一個已經(jīng)生成了的對象),它的狀態(tài)不會發(fā)生從一個狀態(tài)過渡到另一個狀態(tài)的情況,也就是說它的狀態(tài)只有一個,執(zhí)行完畢后即結(jié)束,不存在多狀態(tài)的問題;如果采用責(zé)任鏈模式,則可以以交易編碼作為鏈中的判斷依據(jù),由每個執(zhí)行節(jié)點(diǎn)進(jìn)行判斷,返回相應(yīng)的扣款模式。但是在實際中,采用了關(guān)系型數(shù)據(jù)庫存儲扣款規(guī)則與交易編碼的對應(yīng)關(guān)系,為了簡化該部分的講義,我們在下面的設(shè)計中使用了條件判斷語句來代替。
還有,我們分析了這么多,這么復(fù)雜的扣款模塊總要進(jìn)行一個封裝吧,你不能讓上層的業(yè)務(wù)模塊直接深入到我們模塊的內(nèi)部吧,于是門面模式又?jǐn)[在了眼前。
分析完畢,我們要先畫出類圖,挑柿子就選軟的捏,那我們做設(shè)計也遵循一下這個原則,先選最簡單的業(yè)務(wù),然后畫出類圖,那我們先定義交易中使用到的兩個類:IC卡類和交易類。
每個IC卡有三個屬性,分別是IC卡號碼、固定金額、自由金額,然后通過getter/setter方法來訪問。
細(xì)心的讀者可能注意到,你的金額怎么都是整數(shù)類型呀,這不對呀,應(yīng)該是double類型或者BigDecimal類型呀。是,一般非銀行的交易系統(tǒng),比如超市的收銀系統(tǒng),系統(tǒng)內(nèi)都是存放的int整數(shù)類型,在顯示的時候才轉(zhuǎn)換為貨幣類型。
交易信息Trade類,負(fù)責(zé)記錄每一筆交易,它是由監(jiān)聽程序監(jiān)聽MQ隊列而產(chǎn)生的,有兩個屬性:交易編號和交易金額,其中的交易編號對整個交易非常重要,18位字符(在銀行的交易系統(tǒng)中,這里可不是字符串,一般是十進(jìn)制數(shù)組或二進(jìn)制數(shù)字,要考慮系統(tǒng)的性能,數(shù)字運(yùn)算可比字符運(yùn)算快得多),包括POS機(jī)編號、商戶編號、校驗碼等等,我們這里暫時用不到,就不多做介紹,我們只要知道它是一個非常有用的編碼就成。交易金額為整數(shù)類型,實際金額放大100倍即可。
我們回顧一下,我們在該案例中使用了幾個模式:
1.策略模式
負(fù)責(zé)對扣款策略進(jìn)行封裝,保證兩個策略是可以自由切換的,而且日后增加扣款策略也是非常簡單容易的。
2. 工廠方法模式
修正策略模式必須對外暴露具體策略的問題,由工廠方法模式直接產(chǎn)生一個具體策略對象,而其他模塊則不需要依賴具體的策略。
3.門面模式
負(fù)責(zé)對復(fù)雜的扣款系統(tǒng)進(jìn)行封裝,封裝的結(jié)果就是避免高層模塊深入子系統(tǒng)內(nèi)部,同時提供系統(tǒng)的高內(nèi)聚、低耦合的特性。
我們主要使用了這三個模式,好處是什么呢?靈活,穩(wěn)定,我們可以設(shè)想一下,可能有哪些業(yè)務(wù)變化?
4.扣款策略變更
這個沒問題,增加一個新扣款策略,三步就可以完成:實現(xiàn)IDeduction接口,增加StrategyMan配置項,擴(kuò)展扣款策略的利用(也就是門面模式的的getDeductionType方法,在實際的項目中這里只需要增加數(shù)據(jù)庫的配置項),那減少一個策略呢?簡單,修改扣款策略的利用,那變更一個扣款策略呢?也簡單,擴(kuò)展一個實現(xiàn)類口可以了。
5.變更扣款策略的利用規(guī)則
也簡單,我們的系統(tǒng)不想大修改,還記得我們提出的狀態(tài)模式嗎?這個就是為策略的利用服務(wù)的,變更它就能滿足你的要求,想把IC卡也納入策略利用的規(guī)則,也簡單,不復(fù)雜。其實這個變更還真發(fā)生了,系統(tǒng)投產(chǎn)后,業(yè)務(wù)提出考慮退休人員的情況,退休人員的IC卡與普通在職員工一樣,但是它的扣款不僅僅是根據(jù)交易編碼,還要根據(jù)IC卡對象,系統(tǒng)的變更做法是增加一個扣款策略,同時擴(kuò)展扣款利用策略,也就是數(shù)據(jù)庫的配置項,在getDeductionType中新擴(kuò)展了一個功能:根據(jù)IC卡號,確認(rèn)是否是退休人員,是退休人員,則使用新的扣款策略,非常簡單的擴(kuò)展。
這就是一個mini版的金融交易系統(tǒng),沒啥復(fù)雜的,剩下的問題就開始考慮系統(tǒng)的魯棒性吧,這才是難點(diǎn)。在項目中快樂路徑是最容易實現(xiàn)的,一旦融入悲傷路徑,系統(tǒng)的復(fù)雜性就大大提高,所以呀,才有需求分析時要先要描述一個成功場景,然后再一步一步地增加擴(kuò)展場景的迭代分析模式,否則一上來就考慮一堆的擴(kuò)展場景,這會讓你思維混亂、手足無措,直接進(jìn)入郁悶假死狀態(tài)。
分享到微信 ×
打開微信,點(diǎn)擊底部的“發(fā)現(xiàn)”,
使用“掃一掃”即可將網(wǎng)頁分享至朋友圈。