问题描述
在多个接口长的一样的情况下,或者一个接口有多个实现类的情况下。使用策略模式(state/strategy)重构分支语句。
主要有两个方法:
- 创建枚举类;
- 在接口中定义用于返回区分各个实现类的channel()函数。
第一种枚举类的方法适用于实现类采用不同算法的情况,不需要使用@Autowired
自动注入的情况;
第二种方法适用于需要自动注入,new一个实现类无法实现自动注入的情况。
先随便举个栗子作为重构前的代码:
switch(customer){
case "common":
price = commonCustomer.contPrice(productPrice,num);
break;
case "lv1":
price = levelOneCustomer.contPrice(productPrice,num);
break;
case "lv2":
price = levelTwoCustomer.contPrice(productPrice,num);
break;
case "VIP":
price = VIPCustomer.contPrice(productPrice,num);
break;
default:
break;
}
一个接口就对应了一个case,接口内的每一个函数往往都需要这样一个switch,这样每多一种情况就要在各个switch中查找修改,任务量大不说,还容易出错
创建枚举法
1.合并接口
在处理前接口和实现类往往是一对一关系,接口的存在意义仿佛被淡化了。将原先多个实现类合并为一个接口的实现类:
public interface CustomerService{
Double contPrice(double productPrice,double num);
}
case分支的接口改为其实现类,这里选择VIPCustomer
为例:
public class VIPCustomer implements CustomerService {
@Override
public Double contPrice(double productPrice,double num) {
return 0.6 * productPrice * num;
}
}
2.创建枚举类
public enum customerType {
common,
lv1,
lv2,
VIP;
}
3.创建Map
public class PriceController {
static Map<customerType, CustomerService> customerServiceMap = new HashMap<>();
static {
customerServiceMap.put(customerType.common,new commonCustomer());
customerServiceMap.put(customerType.lv1,new levelOneCustomer());
customerServiceMap.put(customerType.lv2,new levelTwoCustomer());
customerServiceMap.put(customerType.VIP,new VIPCustomer());
}
public Double contPrice(String customerType,double productPrice,double num) {
// 根据字符串顾客类型获取到对应的枚举类型
customerType customer = customerType.valueOf(customerType);
// 从Map中拿到操作类型对应的策略类,如果没有,则默认返回DefaultTask,没有DefaultTask也可以直接用get
CustomerService customerService = customerServiceMap.getOrDefault(customer, new DefaultTask());
// 返回结果
return customerService.contPrice(productPrice,num);
}
}
以上改造适用于算法类,如果实现类还需要调用Mapper等操作,可能需要自动注入的,在static生成时,new出来的实现类无法实现自动注入,在运行时会出现空指针异常,请采用下面的方法。
返回标志法
将上面的栗子改为从数据库中查找商品对应的价格,则Impl类就变成下面这个样子:
public class VIPCustomer implements CustomerService {
@Autowired
productServiceMapper productMapper;
@Override
public Double contPrice(String productName,double num) {
double productPrice = productMapper.getPriceByName(productName);
return 0.6 * productPrice * num;
}
}
此时由于需要@Autowired
自动注入,之前new一个实现类由于静态代码在自动注入之前完成,因此无法实现自动注入。
1.集合保存实现类
首先通过Set<CustomerService>
将CustomerService
接口的所有实现类都注入集合customerServiceSet
中。
@Autowired
Set<CustomerService> customerServiceSet;
2.存储映射关系
接着使用Map<String, CustomerService>
存储调用实现类和原先每个case
之间的映射关系,并初始化
Map<String,CustomerService> customerServiceMap = new HashMap<>();
@PostConstruct
public void init() {
for (CustomerService customerService : customerServiceSet) {
customerServiceMap.put(customerService.channel(), customerService);
}
}
使用@PostConstruct
注解修饰的init
方法会在Spring容器的启动时自动的执行,在其中完成映射Map的构建。
为避免看不懂这些代码放在哪,把完整的Controller
写一下:
public class PriceController{
@Autowired
Set<CustomerService> customerServiceSet;
Map<String,CustomerService> customerServiceMap = new HashMap<>();
public Double contPrice(String customerType,double productPrice,double num) {
// 根据字符串顾客类型获取到对应实现类
CustomerService customerService = customerServiceMap.get(customerType);
// 返回结果
return customerService.contPrice(productPrice,num);
}
@PostConstruct
public void init() {
for (CustomerService customerService : customerServiceSet) {
customerServiceMap.put(customerService.channel(), customerService);
}
}
}
3.接口定义返回标志
public interface CustomerService{
Double contPrice(double productPrice,double num);
String channel();
}
4.实现类区分返回值
public class VIPCustomer implements CustomerService {
@Override
public String channel(){
return "VIP";
}
}
大功告成!