研磨設計模式 之 裝飾模式(Decorator)1

22.1  場景問題

22.1.1  複雜的獎金計算

       考慮這樣一個實際應用:就是如何實現靈活的獎金計算。html

獎金計算是相對複雜的功能,尤爲是對於業務部門的獎金計算方式,是很是複雜的,除了業務功能複雜外,另一個麻煩之處是計算方式還常常須要變更,由於業務部門常常經過調整獎金的計算方式來激勵士氣。算法

       先從業務上看看現有的獎金計算方式的複雜性:數據庫

  • 首先是獎金分類:對於我的,大體有我的當月業務獎金、我的累計獎金、我的業務增加獎金、及時回款獎金、限時成交加碼獎金等等;設計模式

  • 對於業務主管或者是業務經理,除了我的獎金外,還有:團隊累計獎金、團隊業務增加獎金、團隊盈利獎金等等。學習

  • 其次是計算獎金的金額,又有這麼幾個基數:銷售額、銷售毛利、實際回款、業務成本、獎金基數等等;測試

  • 另一個就是計算的公式,針對不一樣的人、不一樣的獎金類別、不一樣的計算獎金的金額,計算的公式是不一樣的,就算是同一個公式,裏面計算的比例參數也有多是不一樣的。this

22.1.2  簡化後的獎金計算體系

看了上面獎金計算的問題,所幸咱們只是來學習設計模式,並非真的要去實現整個獎金計算體系的業務,所以也沒有必要把全部的計算業務都羅列在這裏,爲了後面演示的須要,簡化一下,演示用的獎金計算體系以下:spa

  • 每一個人當月業務獎金 = 當月銷售額 X  3%.net

  • 每一個人累計獎金 = 總的回款額 X  0.1%設計

  • 團隊獎金 = 團隊總銷售額 X 1%

22.1.3  不用模式的解決方案

一我的的獎金分紅不少個部分,要實現獎金計算,主要就是要按照各個獎金計算的規則,把這我的能夠獲取的每部分獎金計算出來,而後計算一個總和,這就是這我的能夠獲得的獎金。

(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

22.1.4  有何問題

看了上面的實現,挺簡單的嘛,就是計算方式麻煩點,每一個規則都要實現。真的很簡單嗎?仔細想一想,有沒有什麼問題?

對於獎金計算,光是計算方式複雜,也就罷了,不過是實現起來會困難點,相對而言仍是比較好解決的,不過是用程序把已有的算法表達出來。

最痛苦的是,這些獎金的計算方式,常常發生變更,幾乎是每一個季度都會有小調整,每一年都有大調整,這就要求軟件的實現要足夠靈活,要可以很快進行相應調整和修改,不然就不能知足實際業務的須要。

       舉個簡單的例子來講,如今根據業務須要,須要增長一個「環比增加獎金」,就是本月的銷售額比上個月有增長,並且要達到必定的比例,固然增加比例越高,獎金比例越大。那麼軟件就必需要從新實現這麼個功能,並正確的添加到系統中去。過了兩個月,業務獎勵的策略發生了變化,再也不須要這個獎金了,或者是另外換了一個新的獎金方式了,那麼軟件就須要把這個功能從軟件中去掉,而後再實現新的功能。

       那麼上面的要求該如何實現呢?

       很明顯,一種方案是經過繼承來擴展功能;另一種方案就是到計算獎金的對象裏面,添加或者刪除新的功能,並在計算獎金的時候,調用新的功能或是不調用某些去掉的功能,這種方案會嚴重違反開-閉原則。

       還有一個問題,就是在運行期間,不一樣人員參與的獎金計算方式也是不一樣的,舉例來講:若是是業務經理,除了參與我的計算部分外,還要參加團隊獎金的計算,這就意味着須要在運行期間動態來組合須要計算的部分,也就是會有一堆的if-else。

       總結一下,獎金計算面臨以下問題:

  • (1)計算邏輯複雜

  • (2)要有足夠靈活性,能夠方便的增長或者減小功能

  • (3)要能動態的組合計算方式,不一樣的人蔘與的計算不一樣

上面描述的獎金計算的問題,絕對沒有任何誇大成分,相反已經簡化很多了,還有更多麻煩沒有寫上來,畢竟咱們的重點在設計模式,而不是業務。

把上面的問題抽象一下,設如有一個計算獎金的對象,如今須要可以靈活的給它增長和減小功能,還須要可以動態的組合功能,每一個功能就至關於在計算獎金的某個部分。

       如今的問題就是:如何纔可以透明的給一個對象增長功能,並實現功能的動態組合呢?

 


原創內容,轉載請註明出處【http://sishuok.com/forum/blogPost/list/0/5764.html

相關文章
相關標籤/搜索