策略模式
首先定义一个接口:
public interface PayService {
BigDecimal quote(BigDecimal price);
}
定义几个策略类:
@Service
public class VipPayService implements PayService, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.register("vip", this);
}
@Override
public BigDecimal quote(BigDecimal price) {
return price.multiply(BigDecimal.valueOf(0.8));
}
}
@Service
public class CommonPayService implements PayService, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.register("common", this);
}
@Override
public BigDecimal quote(BigDecimal price) {
return price.multiply(BigDecimal.valueOf(1.0));
}
}
策略模式有一个缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。这里引入工厂模式。
工厂模式
定义工厂类:
public class StrategyFactory {
private static Map<String, PayService> serviceMap = new HashMap<>();
public static PayService getService(String name) {
return serviceMap.get(name);
}
public static void register(String name, PayService service) {
Assert.notNull(service, "pay service can't be null");
serviceMap.put(name, service);
}
}
测试
定义控制器:
@ApiOperation(value = "计算金额", notes = "计算金额")
@GetMapping("price")
public BigDecimal getPrice(@RequestParam String name) {
return StrategyFactory.getService(name).quote(BigDecimal.valueOf(100));
}
1、访问:http://localhost:8080/user/price?name=vip
,返回:
{
"code": 0,
"message": "成功",
"data": 80
}
2、访问:http://localhost:8080/user/price?name=common
,返回:
{
"code": 0,
"message": "成功",
"data": 100
}
优化
策略类需要实现afterPropertiesSet
方法,需要暴露StrategyFactory
注入接口,这里我们通过修改StrategyFactory
类,实现策略类只处理业务逻辑。
@Component
public class StrategyFactory implements InitializingBean, ApplicationContextAware {
private static Map<String, PayService> serviceMap = new HashMap<>();
private ApplicationContext context;
public PayService getService(String name) {
return serviceMap.get(name);
}
@Override
public void afterPropertiesSet() throws Exception {
context.getBeansOfType(PayService.class)
.values()
.forEach(service -> serviceMap.put(service.getName(), service));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
- ApplicationContextAware:通过它Spring容器会自动把上下文环境对象调用ApplicationContextAware接口中的setApplicationContext方法。这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。
- 在
prepareBeanFactory(beanFactory)
方法装载ApplicationContextAwareProcessor
- 在
finishBeanFactoryInitialization
方法遍历BeanProccessor
调用postProcessBeforeInitialization
方法,该方法再调用setApplicationContext
将ApplicationContext设置到Aware里面。
- 在
让 StrategyFactory
实现 InitializingBean
接口,在 afterPropertiesSet
方法中,基于 Spring 容器将所有PayService
自动注册到serviceMap
,Spring容器启动后,getService方法可以直接通过name来获取对应的实现类。
策略类接口:
public interface PayService {
String getName();
BigDecimal quote(BigDecimal price);
}
策略类实现类:
@Service
public class VipPayService implements PayService {
@Override
public String getName() {
return "vip";
}
@Override
public BigDecimal quote(BigDecimal price) {
return price.multiply(BigDecimal.valueOf(0.8));
}
}
使用:
@Autowired
private StrategyFactory strategyFactory;
@ApiOperation(value = "计算金额", notes = "计算金额")
@GetMapping("price")
public BigDecimal getPrice(@RequestParam String name) {
return strategyFactory.getService(name).quote(BigDecimal.valueOf(100));
}
总结
本文,我们通过策略模式、工厂模式以及Spring的InitializingBean,提升了代码的可读性以及可维护性,彻底消灭了一坨if-else。