标签:参数 conf cep 工具类 key context 方法 @param 接口
不知大家有没遇到过像横放着的金字塔一样的if-else嵌套:
if (true) {
    if (true) {
        if (true) {
            if (true) {
                if (true) {
                    if (true) {
                        
                    }
                }
            }
        }
    }
}if-else作为每种编程语言都不可或缺的条件语句,我们在编程时会大量的用到。
但if-else一般不建议嵌套超过三层,如果一段代码存在过多的if-else嵌套,代码的可读性就会急速下降,后期维护难度也大大提高。
前阵子重构了服务费收费规则,重构前的if-else嵌套如下。
public Double commonMethod(Integer type, Double amount) {
    if (3 == type) {
        // 计算费用
        if (true) {
            // 此处省略200行代码,包含n个if-else,下同。。。
        }
        return 0.00;
    } else if (2 == type) {
        // 计算费用
        return 6.66;
    }else if (1 == type) {
        // 计算费用
        return 8.88;
    }else if (0 == type){
        return 9.99;
    }
    throw new IllegalArgumentException("please input right value");
}我们都写过类似的代码,回想起被 if-else 支配的恐惧,如果有新需求:新增计费规则或者修改既定计费规则,无所下手。
CalculationUtil.getFee(type, amount)或者
serviceFeeHolder.getFee(type, amount)是不是超级简单,下面介绍两种实现方式(文末附示例代码)。
我们拥有很多公司会员,暂且分为普通会员、初级会员、中级会员和高级会员,会员级别不同计费规则不同。该模块负责计算会员所需的缴纳的服务费。
用于维护会员类型。
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;
    }
}该接口包含两个方法:
public interface FeeService {
    /**
     * 计费规则
     * @param amount 会员的交易金额
     * @return
     */
    Double compute(Double amount);
    /**
     * 获取会员级别
     * @return
     */
    Integer getType();
}<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>这里四个子类实现了策略接口,其中 compute()方法实现各个级别会员的计费逻辑,getType()指定了该类所属的会员级别。
public 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();
    }
}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 IntermediateMember implements FeeService {
    /**
     * 计算中级会员所需缴费的金额
     * @param amount 会员的交易金额
     * @return
     */
    @Override
    public Double compute(Double amount) {
        // 具体的实现根据业务需求修改
        return 6.66;
    }
    @Override
    public Integer getType() {
        return MemberEnum.INTERMEDIATE_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,该工厂类管理所有的策略接口实现类。具体见代码注释。
public class ServiceFeeFactory {
    private Map<Integer, FeeService> map;
    public ServiceFeeFactory() {
        // 该工厂管理所有的策略接口实现类
        List<FeeService> feeServices = new ArrayList<>();
        feeServices.add(new OrdinaryMember());
        feeServices.add(new JuniorMember());
        feeServices.add(new IntermediateMember());
        feeServices.add(new SeniorMember());
        // 把所有策略实现的集合List转为Map
        map = new ConcurrentHashMap<>();
        for (FeeService feeService : feeServices) {
            map.put(feeService.getType(), feeService);
        }
    }
    /**
     * 静态内部类单例
     */
    public static class Holder {
        public static ServiceFeeFactory instance = new ServiceFeeFactory();
    }
    /**
     * 在构造方法的时候,初始化好 需要的 ServiceFeeFactory
     * @return
     */
    public static ServiceFeeFactory getInstance() {
        return Holder.instance;
    }
    /**
     * 根据会员的级别type 从map获取相应的策略实现类
     * @param type
     * @return
     */
    public FeeService get(Integer type) {
        return map.get(type);
    }
}新建通过一个工具类管理计费规则的调用,并对不符合规则的公司级别输入抛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的业务判断。
public class DemoTest {
    @Test
    public void test() {
        Double fees = upMethod(1,20000.00);
        System.out.println(fees);
        // 会员级别超范围,抛 IllegalArgumentException
        Double feee = upMethod(5, 20000.00);
    }
    public Double upMethod(Integer type, Double amount) {
        // getFee()是暴露给用户的的计算方法
        return CalculationUtil.getFee(type, amount);
    }
}8.88
java.lang.IllegalArgumentException: please input right value上述方法无非是借助策略模式+工厂模式+单例模式实现,但是实际场景中,我们都已经集成了Spring Boot,这一段就看一下如何借助Spring Boot更简单实现本次的优化。
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>这部分是与上面区别在于:把策略的实现类得是交给Spring 容器管理
@Component
public 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();
    }
}@Component
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();
    }
}@Component
public 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();
    }
}@Component
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();
    }
}思考:程序如何通过一个标识,怎么识别解析这个标识,找到对应的策略实现类?
我的方案是:在配置文件中制定,便于维护。
alias:
  aliasMap:
    first: ordinaryMember
    second: juniorMember
    third: intermediateMember
    fourth: seniorMember@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "alias")
public class AliasEntity {
    private HashMap<String, String> aliasMap;
    public HashMap<String, String> getAliasMap() {
        return aliasMap;
    }
    public void setAliasMap(HashMap<String, String> aliasMap) {
        this.aliasMap = aliasMap;
    }
    /**
     * 根据描述获取该会员对应的别名
     * @param desc
     * @return
     */
    public String getEntity(String desc) {
        return aliasMap.get(desc);
    }
}该类为了便于读取配置,因为存入的是Map的key-value值,key存的是描述,value是各级别会员Bean的别名。
@Component
public 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;
    }
}亮点:
@SpringBootTest
@RunWith(SpringRunner.class)
public class DemoTest {
    @Resource
    ServiceFeeHolder serviceFeeHolder;
    @Test
    public void test() {
         // 计算应缴纳费用
        System.out.println(serviceFeeHolder.getFee("second", 1.333));
        // 获取会员标志
        System.out.println(serviceFeeHolder.getType("second"));
        // 会员描述错误,抛 IllegalArgumentException
        System.out.println(serviceFeeHolder.getType("zero"));
    }
}8.88
1
java.lang.IllegalArgumentException: please input right value两种方案主要参考了设计模式中的策略模式,因为策略模式刚好符合本场景:
Context叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化,对应本文的ServiceFeeFactory.java。
定义算法的接口,对应本文的FeeService.java。
标签:参数 conf cep 工具类 key context 方法 @param 接口
原文地址:https://www.cnblogs.com/CQqfjy/p/12425023.html