Decorator 裝飾器模式 -動態組合
阿新 • • 發佈:2020-10-12
一:業務場景
獎金計算,每個部門,有不同的計算方法,且每個部門有不同型別的獎金項;而且每年或每隔幾個季度獎金演算法都要重新實現下。
這個應用場景應該比較合適了。
我先列出幾個計算公式:
每人當月的業務獎金 = 當月銷售額 * 3%;
每人累計獎金 = 總的回款額*0.1%;
團隊獎金 = 團隊銷售總額*1%;(只有經理才有哦)
二:不用設計模式,我們程式如下實現:
這裡演示程式碼,就不用資料庫了
/** * 在記憶體中模擬DB,準備好測試資料 * @author dy * @since 2016-08-11 & JDK 1.8.0_91 */ public class TempDB { /** * 記錄每人每月銷售額,這裡為了演示,只有人員,沒有月份 * 這裡演示 假設每人每月銷售業務金額都是相同的哈 */ public static Map<String,Double> mapMonthSaleMoney = new HashMap<>(); public static Map<String,String> saleManager = new HashMap<>(); static { mapMonthSaleMoney.put("楊過", 10000.00); mapMonthSaleMoney.put("令狐沖", 20000.00); mapMonthSaleMoney.put("韋小寶", 30000.00); mapMonthSaleMoney.put("dy", 10000.00); saleManager.put("經理", "dy"); } }
接下來寫不同獎金計算方法
/** * 計算某人當月的業務獎金 * * @param user * @param month * @return */ private double monthPrize(String user, int month) { //當月業務獎金 = 業務額*3% double prize = TempDB.mapMonthSaleMoney.get(user) * 0.03; System.out.println(user + " " + month + "月份業務獎金:" + prize); return prize; } /** * 計算某人當月累計獎金 * * @param user * @param month * @return */ private double sumPrize(String user, int month) { //這裡演示 假設每人每月銷售業務金額都是相同的哈 //累計業務獎金 = 累計業務金額*0.1% double prize = TempDB.mapMonthSaleMoney.get(user) * 0.001; System.out.println(user + " " +month+"月份累計獎金:"+prize); return prize; } /** * 是否是業務經理 * * @param user * @return */ private boolean isManager(String user) { return TempDB.saleManager.get("經理").equals(user); } /** * 計算當月團隊獎金 * * @param user * @param month * @return */ private double groupPrize(String user, int month) { if (!isManager(user)){ return 0; } //團隊獎金 = 團隊總業務額 * 1% double prize = 0.00; for (double amount : TempDB.mapMonthSaleMoney.values()) { prize += amount; } prize = prize * 0.01; System.out.println(user + " " +month+"月份團隊獎金:"+prize); return prize; }
ok,我們開始計算獎金吧
/** * 計算獎金 * @param month * @return */ public void calcPrize(String user,int month) { double monthPrize = this.monthPrize(user, month); double sumPrize = this.sumPrize(user, month); double groupPrize =groupPrize(user, month); System.out.println(user + " " +month+"月份應得獎金:"+(monthPrize+sumPrize+groupPrize)+"==>>"); }
客戶端測試
public class Client { public static void main(String[] args) { Prize prize = new Prize(); for (String user:TempDB.mapMonthSaleMoney.keySet()){ prize.calcPrize(user,3); } } }
測試結果:
以上程式碼有何問題??
如果我們計算獎金演算法變了呢??如果我們要增加新的獎金項? 如果我們要移除累計獎金項呢??等等
我們是不是需要頻繁改動程式碼呢?作為一個老鳥程式設計師,我們要求程式要更加靈活、支援可擴充套件!
如果做到呢?
我們把問題抽象下,假如有個獎金物件,可以支援靈活的增加或減少不同的獎金項,就意味著獎金物件的功能可以動態組合,這樣是不是會更好些呢?
三:使用裝飾模式,
動態地為一個物件增加或減少一些職責。就增加職責來說,比子類更加靈活。
如下是裝飾模式結構說明:
ok,讓我們利用裝飾模式,重新實現計算獎金吧!
/** * 計算金額的元件介面 * @author dy * @since 2016-08-11 & JDK 1.8.0_91 */ public abstract class Component { /** * 計算獎金 * @param month */ public abstract double calcPrize(String user,int month); }
/** * 基本的實現類,預設實現,也是被裝飾的物件 * @author dy * @since 2016-08-11 & JDK 1.8.0_91 */ public class ConcreteComponent extends Component { @Override public double calcPrize(String user,int month) { System.out.println("預設沒有獎金!"); return 0; } }
/** * 裝飾器 * 需要和被裝飾的物件實現同樣的介面 * @author dy * @since 2016-08-11 & JDK 1.8.0_91 */ public abstract class Decorator extends Component { /** * 持有被裝飾的物件 */ protected Component component; public Decorator(Component component) { this.component = component; } @Override public double calcPrize(String user,int month) { double prize = component.calcPrize(user,month);//轉調元件物件的方法 return prize; } }
/** * 裝飾器物件 ,計算當月業務獎金 * * @author dy * @since 2016-08-11 & JDK 1.8.0_91 */ public class MonthPrizeDecorator extends Decorator { public MonthPrizeDecorator(Component component) { super(component); } @Override public double calcPrize(String user, int month) { double money = super.calcPrize(user, month); double monthPrize = TempDB.mapMonthSaleMoney.get(user) * 0.03; System.out.println(user + " " + month + "月份業務獎金:" + (monthPrize) + "==>>"); return monthPrize + money; } }
/** * 裝飾器物件 ,計算累計獎金 * * @author dy * @since 2016-08-11 & JDK 1.8.0_91 */ public class SumPrizeDecorator extends Decorator { public SumPrizeDecorator(Component component) { super(component); } @Override public double calcPrize(String user, int month) { double money = super.calcPrize(user, month); double sumPrize = TempDB.mapMonthSaleMoney.get(user) * 0.001; System.out.println(user + " " + month + "月份累計獎金:" + (sumPrize) + "==>>"); return sumPrize + money; } }
/** * 裝飾器物件 ,計算累計獎金 * @author dy * @since 2016-08-11 & JDK 1.8.0_91 */ public class GroupPrizeDecorator extends Decorator { public GroupPrizeDecorator(Component component) { super(component); } @Override public double calcPrize(String user,int month) { double money = super.calcPrize(user,month); if (!isManager(user)){ return money; } double totalPrize = 0.00; //團隊獎金 = 團隊總業務額 * 1% double prize = 0.00; for (double amount : TempDB.mapMonthSaleMoney.values()) { prize += amount; } prize = prize*0.01; System.out.println(user + " " +month+"月份團隊獎金:"+(prize)+"==>>"); return money + prize; } private boolean isManager(String user) { return TempDB.saleManager.get("經理").equals(user); } }
以上將不同計算獎金方式拆解成不同元件,需要哪些獎金計算,自動裝置就可以了,讓我們來試試吧!
public class Client { public static void main(String[] args) { for (String user : TempDB.mapMonthSaleMoney.keySet()) { //建立基本獎金物件 Component component = new ConcreteComponent(); /** * 對基本獎金進行裝飾,這裡要組合各個裝飾 */ Decorator decorator1 = new MonthPrizeDecorator(component); Decorator decorator2 = new SumPrizeDecorator(decorator1); Decorator decorator3 = new GroupPrizeDecorator(decorator1); //這裡只需要用最後組合好的物件呼叫業務方法即可 double prize = decorator3.calcPrize(user, 3); System.out.println(user + " " +3+"月份應得獎金:"+prize+"==>>"); } } }
執行結果,和我們原始程式碼結果一模一樣,但本質上卻更加靈活了
以上程式碼呼叫示例
轉載於:https://my.oschina.net/dyyweb/blog/731318