一:业务场景
奖金计算,每个部门,有不同的计算方法,且每个部门有不同类型的奖金项;而且每年或每隔几个季度奖金算法都要重新实现下。
这个应用场景应该比较合适了。
我先列出几个计算公式:
每人当月的业务奖金 = 当月销售额 * 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+"==>>"); } } }
运行结果,和我们原始代码结果一模一样,但本质上却更加灵活了
以上代码调用示例