Decorator 装饰器模式 -动态组合

一:业务场景

奖金计算,每个部门,有不同的计算方法,且每个部门有不同类型的奖金项;而且每年或每隔几个季度奖金算法都要重新实现下。

这个应用场景应该比较合适了。

我先列出几个计算公式:

每人当月的业务奖金 = 当月销售额 * 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值