1. 程式人生 > >業務系統中的開與閉——分發模式

業務系統中的開與閉——分發模式

開閉原則;分發

“對新增開放,對修改關閉。”——開閉原則。

這裏分享一個我在業務系統設計過程中常用的一個“復合模式”,用作一個在業務系統設計中運用“開閉原則”的例子。

背景

這是一個賬務系統,負責處理各類業務流程中發生的若幹個賬戶之間的轉賬相關邏輯,包括賬戶余額的變更、以及各賬戶的流水記錄。
這個系統的復雜度在於:不同的業務流程,所需要操作的賬戶、金額的計算公式、以及流水的類型,都有很大的差異;即使是同一個業務,裏面也會細分為多個子業務,賬戶、金額、流水類型又各不相同。而且,業務、子業務還會不斷的新增和變更。這就要求我們在設計時,必須充分考慮擴展性。

思路

首先,業務流程雖然種類繁多,但是抽象、概括之後,其實核心邏輯非常簡單。第一步當然是校驗,然後是賬戶查找、計算金額等數據準備工作,準備好必要的數據之後就可以記賬了,記賬完成後做一些收尾的操作。

這樣一來,所有的相關業務都可以放到這同一個“抽象”層次內來做處理了。如下圖所示。

技術分享

擴展性問題則是這樣解決的。這個“抽象”層次內,最頂層的“服務提供者”只提供一個分發服務。它會根據外部傳入的參數,選擇對應的業務服務提供者,並將參數交給它來處理。
而“業務服務提供者”的最頂層,同樣只提供分發服務。它會根據一些更細致的規則,選擇對應的子業務服務提供者,然後將參數交給子業務服務提供者去處理。

這樣的服務分發層級,在現實生活中可以找到很直觀的例子。在一些大醫院的門診部大門口,會有一個分診臺。分診臺的護士會告訴你應該掛哪個科室的號。而在一些大科室的門口,又會有一名護士負責叫號,並指點被叫到的病人去找哪位醫生。實際上,這也是我做這個“分發”服務的一個靈感來源。
在這個“分發”的框架下,新增一套業務是非常簡單的。並且,由於真正處理業務功能的服務提供者只需要專註於自己的“一畝三分地”,與其它的業務之間很少發生耦合,因此,在必須對某些功能進行修改時,影響範圍也會非常的小。
理論上,“分發”這個動作可以無限嵌套,因此無論多復雜的業務邏輯,都可以通過不斷的分發處理來進行簡化。只不過這樣做會使得線程處理棧變得非常深,給理解系統和debug和trouble shooting增加不必要的麻煩。我們的系統做到二級分發,已經有“類爆炸”的困擾了。

類圖

技術分享
頂層的BizAccountEventService就是我們匹配“業務抽象”所建立起來的接口。所有的記賬相關業務功能,無論是一級分發、二級分發,還是實際的業務功能處理,都是這個接口下的一個實現類。
只有兩個類直接實現了BizAccountEventService接口,其中之一就是BaeServiceAsDispatcher,即業務抽象內最頂層的“服務提供者”——一級分發器。一級分發器需要一個服務工廠,以根據不同的參數提供不同的接口實現類。這個工廠既可以是一個簡單的Map,也可以做得更復雜一些。我們單獨創建了一個工廠類,這樣可以方便地將一級分發擴展為二級分發。
在一級分發器中,服務工廠提供的類實際上都是二級分發器。二級分發器與一級分發器的邏輯實際上是一樣的,也是利用工廠來根據不同的參數獲得不同的接口實現。因而,二級分發器都繼承自一級分發器,實例之間的差別基本上只是一個工廠。
直接實現BizAccountEventService接口的另一個類是BaeServiceAsSekeleton。這個類是一個模板類,它定義了真正的業務操作的核心步驟:校驗、預處理、轉賬、後處理。所有的業務處理類,都必須是它、或者它的子類的實例。
但是,與教科書上的“模板模式”不同的是,BaeServiceAsSekeleton中的模板模式,並不是通過繼承、而是通過組合來實現的。這個模板中的四個步驟,被分別委托給了三個接口(BaeValidator、BaeEditor、BaeTransfer)。這樣做可以增加不同子業務之間的代碼復用性。例如,業務甲的邏輯是ABCD,業務乙的邏輯是EFGH,而業務丙的則是ABGH。這種情況下,由於Java單繼承的限制,業務丙無法通過繼承甲或乙的類來完全復用代碼,但是組合的方式卻可以輕松做到。
關於這個接口,項目組內曾有過爭論。有同事認為,應當為BaeServiceAsSkeleton類提供另一個接口,以便於保持BizAccountEventService接口的層次簡單。這樣做當然可以,不過我們沒有把精力放在這件事上。

開閉

那麽,回過頭來看“開閉”。“對新增開放”很好理解,那麽什麽是“閉”呢?
實事求是的說,我們不可能完全地把“修改”關在門外。在這個“分發”方案裏,我們也仍然需要通過修改分發相關代碼,來增加新的業務、或變更原有業務。我們能夠關閉的、也許也是我們真正想關閉的,是大量的、大範圍的修改代碼、是這樣做所帶來的不可控的風險。
以這個“分發”方案為例。當一種業務邏輯發生變化時,我們需要修改的僅僅是分發相關代碼、以及這一種子業務所涉及的部分子類。這種修改的影響範圍被牢牢鎖定在業務抽象之下、甚至是單個子業務的相關抽象之下,因而,其中的風險是清晰明了的,因而也是可以有效控制住的。甚至於,我們可以保持原有業務代碼完全變,而通過繼承、多態等方式擴展出新的業務邏輯,從而用“廢除+新增”的方式完全替代“修改”——當然,這種方式會帶來很多冗余代碼,值得商榷。
但是,把不可控的風險關在門外——我們做到了。


本文出自 “編程的摩羯男” 博客,請務必保留此出處http://winters1224.blog.51cto.com/3021203/1959651

業務系統中的開與閉——分發模式