问题描述:在创建合同时,会选择一个何时付款的策略,比如,目前策略有:
合同执行开始时付款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方法,还想假装疏忽没注意,嘿嘿妈妈都不同意。