1:策略模式图解
2:例如重构如下:取代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");
}
3、通用部分
普通会员、初级会员、中级会员和高级会员,会员级别不同计费规则不同。该模块负责计算会员所需的缴纳的服务费。
3.1:会员枚举
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.2:定义一个策略接口
该接口包含两个方法:
compute(Double amount):各计费规则的抽象
getType():获取枚举中维护的会员级别
public interface FeeService {
/**
* 计费规则
* @param amount 会员的交易金额
* @return
*/
Double compute(Double amount);
/**
* 获取会员级别
* @return
*/
Integer getType();
}
3.3:不同计费规则的实现-把策略的实现类得是交给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();
}
}
3.4:策略工厂
@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;
}
}
亮点
1:将 Spring中所有 ServiceFee.java 的实现类注入到Map中,不同策略通过其不同的key获取其实现类;
:2:找不到对应的策略的实现类,抛出IllegalArgumentException异常。
测试
@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: 环境类
Context叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化,对应本文的ServiceFeeFactory.java。
Strategy: 抽象策略类
定义算法的接口,对应本文的FeeService.java。
ConcreteStrategy: 具体策略类
备注:依赖
<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>
参考
https://juejin.im/post/5e5dbe36f265da57455b4a67