作者:Van_Fan 一、背景1.1 反面教材不知大家有没遇到过像横放着的金字塔一样的if-else嵌套: if (true) { if (true) { if (true) { if (true) { if (true) { if (true) { } } } } }} if-else作为每种编程语言都不可或缺的条件语句,我们在编程时会大量的用到。 但if-else一般不建议嵌套超过三层,如果一段代码存在过多的if-else嵌套,代码的可读性就会急速下降,后期维护难度也大大提高。 2.2 亲历的重构前阵子重构了服务费收费规则,重构前的if-else嵌套如下。
我们都写过类似的代码,回想起被 if-else 支配的恐惧,如果有新需求:新增计费规则或者修改既定计费规则,无所下手。 2.3 追根溯源
CalculationUtil.getFee(type, amount) 或者
是不是超级简单,下面介绍两种实现方式(文末附示例代码)。 二、通用部分2.1 需求概括我们拥有很多公司会员,暂且分为普通会员、初级会员、中级会员和高级会员,会员级别不同计费规则不同。该模块负责计算会员所需的缴纳的服务费。 2.2 会员枚举
public enum MemberEnum { ORDINARY_MEMBER(0, '普通会员'), JUNIOR_MEMBER(1, '初级会员'), INTERMEDIATE_MEMBER(2, '中级会员'), SENIOR_MEMBER(3, '高级会员'), ; int code; String desc; MemberEnum(int code, String desc) { this.code = code; this.desc = desc; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; }} 该接口包含两个方法:
三、非框架实现3.1 项目依赖<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope></dependency> 这里四个子类实现了策略接口,其中 compute()方法实现各个级别会员的计费逻辑,getType()指定了该类所属的会员级别。
public class JuniorMember implements FeeService { /** * 计算初级会员所需缴费的金额 * @param amount 会员的交易金额 * @return */ @Override public Double compute(Double amount) { // 具体的实现根据业务需求修改 return 8.88; } @Override public Integer getType() { return MemberEnum.JUNIOR_MEMBER.getCode(); }}
public class SeniorMember implements FeeService { /** * 计算高级会员所需缴费的金额 * @param amount 会员的交易金额 * @return */ @Override public Double compute(Double amount) { // 具体的实现根据业务需求修改 return 0.01; } @Override public Integer getType() { return MemberEnum.SENIOR_MEMBER.getCode(); }} 创建一个工厂类ServiceFeeFactory.java,该工厂类管理所有的策略接口实现类。具体见代码注释。 3.4 工具类新建通过一个工具类管理计费规则的调用,并对不符合规则的公司级别输入抛IllegalArgumentException。 public class CalculationUtil { /** * 暴露给用户的的计算方法 * @param type 会员级别标示(参见 MemberEnum) * @param money 当前交易金额 * @return 该级别会员所需缴纳的费用 * @throws IllegalArgumentException 会员级别输入错误 */ public static Double getFee(int type, Double money) { FeeService strategy = ServiceFeeFactory.getInstance().get(type); if (strategy == null) { throw new IllegalArgumentException('please input right value'); } return strategy.compute(money); }} 核心是通过Map的get()方法,根据传入 type,即可获取到对应会员类型计费规则的实现,从而减少了if-else的业务判断。 3.5 测试
8.88java.lang.IllegalArgumentException: please input right value 4.1 项目依赖 4.2 不同计费规则的实现这部分是与上面区别在于:把策略的实现类得是交给Spring 容器管理
@Componentpublic class OrdinaryMember implements FeeService { /** * 计算普通会员所需缴费的金额 * @param amount 会员的交易金额 * @return */ @Override public Double compute(Double amount) { // 具体的实现根据业务需求修改 return 9.99; } @Override public Integer getType() { return MemberEnum.ORDINARY_MEMBER.getCode(); }}
@Componentpublic class IntermediateMember implements FeeService { /** * 计算中级会员所需缴费的金额 * @param amount 会员的交易金额 * @return */ @Override public Double compute(Double amount) { // 具体的实现根据业务需求修改 return 6.66; } @Override public Integer getType() { return MemberEnum.INTERMEDIATE_MEMBER.getCode(); }}
4.3 别名转换
我的方案是:在配置文件中制定,便于维护。
alias: aliasMap: first: ordinaryMember second: juniorMember third: intermediateMember fourth: seniorMember
4.4 策略工厂 @Componentpublic class ServiceFeeHolder { /** * 将 Spring 中所有实现 ServiceFee 的接口类注入到这个Map中 */ @Resource private Map<String, FeeService> serviceFeeMap; @Resource private AliasEntity aliasEntity; /** * 获取该会员应当缴纳的费用 * @param desc 会员标志 * @param money 交易金额 * @return * @throws IllegalArgumentException 会员级别输入错误 */ public Double getFee(String desc, Double money) { return getBean(desc).compute(money); } /** * 获取会员标志(枚举中的数字) * @param desc 会员标志 * @return * @throws IllegalArgumentException 会员级别输入错误 */ public Integer getType(String desc) { return getBean(desc).getType(); } private FeeService getBean(String type) { // 根据配置中的别名获取该策略的实现类 FeeService entStrategy = serviceFeeMap.get(aliasEntity.getEntity(type)); if (entStrategy == null) { // 找不到对应的策略的实现类,抛出异常 throw new IllegalArgumentException('please input right value'); } return entStrategy; }} 亮点:
8.881java.lang.IllegalArgumentException: please input right value 两种方案主要参考了设计模式中的策略模式,因为策略模式刚好符合本场景:
|
|
来自: 满泉ca85upjdlw > 《Excel知识》