问题描述:在创建合同时,会选择一个何时付款的策略,比如,目前策略有:
? ? 合同执行开始时付款100%,
? ? 合同执行结束后付款100%
? ? 合同每月结算当月应付金额
? ? ...
?
并且付款的策略还会改变。
最初的代码使用一大串if...else...,如下:
class="java" name="code"> 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方法,还想假装疏忽没注意,嘿嘿妈妈都不同意。
?