考慮這樣一個實際應用:就是如何實現靈活的獎金計算。html
獎金計算是相對複雜的功能,尤爲是對於業務部門的獎金計算方式,是很是複雜的,除了業務功能複雜外,另一個麻煩之處是計算方式還常常須要變更,由於業務部門常常經過調整獎金的計算方式來激勵士氣。算法
先從業務上看看現有的獎金計算方式的複雜性:數據庫
首先是獎金分類:對於我的,大體有我的當月業務獎金、我的累計獎金、我的業務增加獎金、及時回款獎金、限時成交加碼獎金等等;設計模式
對於業務主管或者是業務經理,除了我的獎金外,還有:團隊累計獎金、團隊業務增加獎金、團隊盈利獎金等等。學習
其次是計算獎金的金額,又有這麼幾個基數:銷售額、銷售毛利、實際回款、業務成本、獎金基數等等;測試
另一個就是計算的公式,針對不一樣的人、不一樣的獎金類別、不一樣的計算獎金的金額,計算的公式是不一樣的,就算是同一個公式,裏面計算的比例參數也有多是不一樣的。this
看了上面獎金計算的問題,所幸咱們只是來學習設計模式,並非真的要去實現整個獎金計算體系的業務,所以也沒有必要把全部的計算業務都羅列在這裏,爲了後面演示的須要,簡化一下,演示用的獎金計算體系以下:spa
每一個人當月業務獎金 = 當月銷售額 X 3%.net
每一個人累計獎金 = 總的回款額 X 0.1%設計
團隊獎金 = 團隊總銷售額 X 1%
一我的的獎金分紅不少個部分,要實現獎金計算,主要就是要按照各個獎金計算的規則,把這我的能夠獲取的每部分獎金計算出來,而後計算一個總和,這就是這我的能夠獲得的獎金。
(1)爲了演示,先準備點測試數據,在內存中模擬數據庫,示例代碼以下:
/** * 在內存中模擬數據庫,準備點測試數據,好計算獎金 */ public class TempDB { private TempDB(){ } /** * 記錄每一個人的月度銷售額,只用了人員,月份沒有用 */ public static Map<String,Double> mapMonthSaleMoney = new HashMap<String,Double>(); static{ //填充測試數據 mapMonthSaleMoney.put("張三",10000.0); mapMonthSaleMoney.put("李四",20000.0); mapMonthSaleMoney.put("王五",30000.0); } } |
(2)按照獎金計算的規則,實現獎金計算,示例代碼以下:
/** * 計算獎金的對象 */ public class Prize { /** * 計算某人在某段時間內的獎金,有些參數在演示中並不會使用, * 可是在實際業務實現上是會用的,爲了表示這是個具體的業務方法, * 所以這些參數被保留了 * @param user 被計算獎金的人員 * @param begin 計算獎金的開始時間 * @param end 計算獎金的結束時間 * @return 某人在某段時間內的獎金 */ public double calcPrize(String user,Date begin,Date end){ double prize = 0.0; //計算當月業務獎金,全部人都會計算 prize = this.monthPrize(user, begin, end); //計算累計獎金 prize += this.sumPrize(user, begin, end);
//須要判斷該人員是普通人員仍是業務經理,團隊獎金只有業務經理纔有 if(this.isManager(user)){ prize += this.groupPrize(user, begin, end); } return prize; }
/** * 計算某人的當月業務獎金,參數重複,就再也不註釋了 */ private double monthPrize(String user, Date begin, Date end) { //計算當月業務獎金,按照人員去獲取當月的業務額,而後再乘以3% double prize = TempDB.mapMonthSaleMoney.get(user) * 0.03; System.out.println(user+"當月業務獎金"+prize); return prize; }
/** * 計算某人的累計獎金,參數重複,就再也不註釋了 */ public double sumPrize(String user, Date begin, Date end) { //計算累計獎金,其實應該按照人員去獲取累計的業務額,而後再乘以0.1% //簡單演示一下,假定你們的累計業務額都是1000000元 double prize = 1000000 * 0.001; System.out.println(user+"累計獎金"+prize); return prize; }
/** * 判斷人員是普通人員仍是業務經理 * @param user 被判斷的人員 * @return true表示是業務經理,false表示是普通人員 */ private boolean isManager(String user){ //應該從數據庫中獲取人員對應的職務 //爲了演示,簡單點判斷,只有王五是經理 if("王五".equals(user)){ return true; } return false; } /** * 計算當月團隊業務獎,參數重複,就再也不註釋了 */ public double groupPrize(String user, Date begin, Date end) { //計算當月團隊業務獎金,先計算出團隊總的業務額,而後再乘以1%, //假設都是一個團隊的 double group = 0.0; for(double d : TempDB.mapMonthSaleMoney.values()){ group += d; } double prize = group * 0.01; System.out.println(user+"當月團隊業務獎金"+prize); return prize; } } |
(3)寫個客戶端來測試一下,看看是否能正確地計算獎金,示例代碼以下:
public class Client { public static void main(String[] args) { //先建立計算獎金的對象 Prize p = new Prize();
//日期對象都沒有用上,因此傳null就能夠了 double zs = p.calcPrize("張三",null,null); System.out.println("==========張三應得獎金:"+zs); double ls = p.calcPrize("李四",null,null); System.out.println("==========李四應得獎金:"+ls); double ww = p.calcPrize("王五",null,null); System.out.println("==========王經理應得獎金:"+ww); } } |
測試運行的結果以下:
張三當月業務獎金300.0 張三累計獎金1000.0 ==========張三應得獎金:1300.0 李四當月業務獎金600.0 李四累計獎金1000.0 ==========李四應得獎金:1600.0 王五當月業務獎金900.0 王五累計獎金1000.0 王五當月團隊業務獎金600.0 ==========王經理應得獎金:2500.0 |
看了上面的實現,挺簡單的嘛,就是計算方式麻煩點,每一個規則都要實現。真的很簡單嗎?仔細想一想,有沒有什麼問題?
對於獎金計算,光是計算方式複雜,也就罷了,不過是實現起來會困難點,相對而言仍是比較好解決的,不過是用程序把已有的算法表達出來。
最痛苦的是,這些獎金的計算方式,常常發生變更,幾乎是每一個季度都會有小調整,每一年都有大調整,這就要求軟件的實現要足夠靈活,要可以很快進行相應調整和修改,不然就不能知足實際業務的須要。
舉個簡單的例子來講,如今根據業務須要,須要增長一個「環比增加獎金」,就是本月的銷售額比上個月有增長,並且要達到必定的比例,固然增加比例越高,獎金比例越大。那麼軟件就必需要從新實現這麼個功能,並正確的添加到系統中去。過了兩個月,業務獎勵的策略發生了變化,再也不須要這個獎金了,或者是另外換了一個新的獎金方式了,那麼軟件就須要把這個功能從軟件中去掉,而後再實現新的功能。
那麼上面的要求該如何實現呢?
很明顯,一種方案是經過繼承來擴展功能;另一種方案就是到計算獎金的對象裏面,添加或者刪除新的功能,並在計算獎金的時候,調用新的功能或是不調用某些去掉的功能,這種方案會嚴重違反開-閉原則。
還有一個問題,就是在運行期間,不一樣人員參與的獎金計算方式也是不一樣的,舉例來講:若是是業務經理,除了參與我的計算部分外,還要參加團隊獎金的計算,這就意味着須要在運行期間動態來組合須要計算的部分,也就是會有一堆的if-else。
總結一下,獎金計算面臨以下問題:
(1)計算邏輯複雜
(2)要有足夠靈活性,能夠方便的增長或者減小功能
(3)要能動態的組合計算方式,不一樣的人蔘與的計算不一樣
上面描述的獎金計算的問題,絕對沒有任何誇大成分,相反已經簡化很多了,還有更多麻煩沒有寫上來,畢竟咱們的重點在設計模式,而不是業務。
把上面的問題抽象一下,設如有一個計算獎金的對象,如今須要可以靈活的給它增長和減小功能,還須要可以動態的組合功能,每一個功能就至關於在計算獎金的某個部分。
如今的問題就是:如何纔可以透明的給一個對象增長功能,並實現功能的動態組合呢?
原創內容,轉載請註明出處【http://sishuok.com/forum/blogPost/list/0/5764.html】