利用计策枚举重构代码

利用策略枚举重构代码

问题描述:在创建合同时,会选择一个何时付款的策略,比如,目前策略有:

    合同执行开始时付款100%,

    合同执行结束后付款100%

    合同每月结算当月应付金额

    ...

 

并且付款的策略还会改变。

最初的代码使用一大串if...else...,如下:

 public List<PaymentPlan> resolve(ExecutionUnit unit, String paymentTermsNumber) {
        if (unit != null) {
            if (PaymentTermEnum.OTHER.getValue().equals(paymentTermsNumber)) {
                return resolve0(unit);
            } else if (PaymentTermEnum.FIVE_DAY_NEXT_MONTH.getValue().equals(paymentTermsNumber)) {
                return resolve1(unit);
            } else if (PaymentTermEnum.BEFORE_END_MONTH_PAY_PREVIEW_MONTH.getValue().equals(paymentTermsNumber)) {
                return resolve2(unit);
            } else if (PaymentTermEnum.BEFORE_END_MONTH_PAY_NEXT_MONTH.getValue().equals(paymentTermsNumber)) {
                return resolve3(unit);
            } 
            ...
        }
        return new ArrayList<PaymentPlan>();
    }
private List<PaymentPlan> resolve0(ExecutionUnit unit) {
        List<PaymentPlan> paymentPlanList = new ArrayList<PaymentPlan>();
        if (unit == null) {
            return paymentPlanList;
        }
        Contract contract = contractService.getByNumber(unit.getContractnum());
        if (contract == null) {
            return paymentPlanList;
        }
        PaymentPlan filter = new PaymentPlan();
        filter.setContractId(contract.getId());
        return paymentPlanService.list(filter);
    }
....

 和旧社会妇女的裹脚布一样,又臭又长。而且当策略改变的时候,还需要把裹脚布再加长一点,这时很可能疏忽了而没有添加(程序猿们经常会拿疏忽了,忘记了当借口,你懂的)。

当受不了恶心的时候就是进步的前兆,因此决定重构这部分代码,首先代码中判断的时候用到了枚举类PaymentTermEnum,但是里面只有下面这些:

public enum PaymentTermEnum {

    // 其他
    OTHER("0"),
    // 当月广告款次月5日结清
    FIVE_DAY_NEXT_MONTH("1"),
    // 每月30日前向乙方支付上月广告款.
    BEFORE_END_MONTH_PAY_PREVIEW_MONTH("2"),

    // 每月30日前向乙方支付下月广告款
    BEFORE_END_MONTH_PAY_NEXT_MONTH("3"),

    // 每个季度执行完广告后,30日内支付相应的款项
    AFTER_QUARTER_PAY_WITHIN_THIRTY("5"),

    // 每个季度执行完广告后,45日内支付相应的款项.
    AFTER_QUARTER_PAY_WITHIN_FORTH_FIVE("6"),

    // 每个季度执行完广告后,在当季内支付相应的款项.
    AFTER_QUARTER_PAY_WITHIN_THIS_QUARTER("7"),

    // 每个月执行完当月的广告后,30日内支付相应的款项.
    AFTER_END_MONTH_PAY_WITHIN_THIRTY("8"),

    // 每个月执行完当月的广告后,在当月月末支付100%的款项.
    AFTER_END_MONTH_PAY_END_THIS_MONTH("9"),

    // 每张执行单执行前预付100%的款项.
    PAY_BEFORE_EXECUTE("10"),

    // 每张执行单执行前预付30%的款项,执行完付剩余70%的款项.
    PAY_THIRTY_BEFORE_EXECUTE_SEVENTY_AFTER_EXECUTED("11"),

    // 每张执行单执行前预付50%的款项,执行完付剩余50%的款项.
    PAY_HALF_BEFORE_EXECUTE_HALF_AFTER_EXECUTED("12"),

    // 每张执行单执行完广告后,30日内支付相应的款项.
    AFTER_EXECUTED_PAY_WITHIN_THIRTY_DAY("13"),
    // 每张执行单执行完广告后,5个工作日内支付相应的款项.
    AFTER_EXECUTED_PAY_WITHIN_FIVE_DAY("14");

    private String value;

    private PaymentTermEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }
}

 这只能避免在解析的方法中不使用类似于"0","1"这种tricky的字符串。虽然有点小用,但是没有完全发挥枚举的其他作用。

首先,每个策略最终都是要解析成 List<PaymentPlan>,即付款计划的列表,可以在枚举类中使用一个抽象方法,这样枚举中的每个常量必须要实现,此时枚举类变成:

public enum PaymentTermEnum {
    // 其他
    OTHER("0") {
        @Override
        public List<PaymentPlan> resolve(ExecutionUnit unit) {
            ...
        }
    },
    // 当月广告款次月5日结清
    FIVE_DAY_NEXT_MONTH("1") {
        @Override
        public List<PaymentPlan> resolve(ExecutionUnit unit) {
            ...
        }
    },
    // 每月30日前向乙方支付上月广告款.
    BEFORE_END_MONTH_PAY_PREVIEW_MONTH("2") {
        @Override
        public List<PaymentPlan> resolve(ExecutionUnit executionUnit) {
            ...
        }
    },
	...
    ;

    // 解析付款计划
    public abstract List<PaymentPlan> resolve(ExecutionUnit executionUnit);

    private String value;

    private static ExecutionUnitService executionUnitService = ApplicationContextUtil.getContext().getBean(
            ExecutionUnitService.class);
    private static ContractService contractService = ApplicationContextUtil.getContext().getBean(ContractService.class);
    private static PaymentPlanService paymentPlanService = ApplicationContextUtil.getContext().getBean(
            PaymentPlanService.class);

    private static Map<String, PaymentTermEnum> valueMap = new HashMap<String, PaymentTermEnum>();

    static {
        for (PaymentTermEnum paymentTermEnum : PaymentTermEnum.values()) {
            valueMap.put(paymentTermEnum.value, paymentTermEnum);
        }
    }

    public static PaymentTermEnum fromValue(String value) {
        return valueMap.get(value);
    }

    private PaymentTermEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }
}

 解析的Service类的方法就可以简化成:

    public List<PaymentPlan> resolve(ExecutionUnit unit, String paymentTermsNumber) {
        PaymentTermEnum paymentTermEnum = PaymentTermEnum.fromValue(paymentTermsNumber);
        if (paymentTermEnum != null) {
            return paymentTermEnum.resolve(unit);
        }
        return new ArrayList<PaymentPlan>();
    }

 当要添加解析策略的时候,只需要在枚举类中添加一个新的常量即可,而此时编译器会强制我们实现resolve方法,还想假装疏忽没注意,嘿嘿妈妈都不同意。