你是否有过类似如下的业务代码,每种业务类型都有不同的处理数据,但是要返回的结果功能是一样的:
/**
* 根据课程名字查询价格
* @param courseName 课程名字
* @return 课程价格
*/
public BigDecimal findPriceByName(String courseName){
if (StringUtils.isNotBlank(courseName)){
//如果前端参数为English
if (CourseType.ENGLISH.getCourseName().equals(courseName)){
return this.englishService.getEnglishPrice(courseName);
}
//如果前端参数为Chinese
if (CourseType.CHINESE.getCourseName().equals(courseName)){
return this.chineseService.getChinesePrice(courseName);
}
//如果前端参数为Math
if (CourseType.MATH.getCourseName().equals(courseName)){
return this.mathService.getMathPrice(courseName);
}
//..........
}
}
那么如果随着公司的业务线不断增大,或许以后增加至几十种业务产品,当然,我所举得代码例子过于简单,但是不碍于我表达这类似的业务问题。随着产品业务增加,是不是每次都要来修改这又臭又长的if…else…,像一个肿瘤一样。这时候其实根据业务需求改善一下,比如采用策略模式
来重构一下,结构变得清晰很多。我就以最简单的一个例子说起,根据课程的名字查询课程的价格,每一种产品的查询方式或者需要做数据处理不一致,但是功能都是查询价格,不妨将这公共的功能抽取出来,借助Spring的IOC容器因地制宜地解决此问题。
/**
* 课程策略接口
*/
public interface CourseStrategy {
/**
* 根据课程名字查询价格
* @param courseName 课程名字
* @return 课程价格
*/
BigDecimal findPriceByName(String courseName);
}
@Service
public class ChineseService implements CourseStrategy {
@Override
public BigDecimal findPrice() {
return new BigDecimal(300);//业务查询逻辑,我就举个简单例子了
}
}
@Service
public class MathService implements CourseStrategy {
@Override
public BigDecimal findPrice() {
return new BigDecimal(200);//业务查询逻辑,我就举个简单例子了
}
}
@Service
public class EnglishService implements CourseStrategy {
@Override
public BigDecimal findPrice() {
return new BigDecimal(100);//业务查询逻辑,我就举个简单例子了
}
}
public enum CourseType {
ENGLISH("English","englishService"),
CHINESE("Chinese","chineseService"),
MATH("Math","mathService");
private String courseName;
private String serviceName;
CourseType(String courseName,String serviceName){
this.courseName = courseName;
this.serviceName = serviceName;
}
public static String getServiceBeanName(String courseName) {
if (StringUtils.isNotBlank(courseName)) {
for (CourseType course : CourseType.values()) {
if (courseName.equals(course.getCourseName())) {
return course.getServiceName();
}
}
}
return null;
}
public String getCourseName(){ return courseName;}
public String getServiceName(){ return serviceName;}
}
通过IOC容器进行不同的实现不同的处理逻辑
/**
* 课程服务
*/
@Service
public class CourseManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 根据课程名字查询价格
* @param courseName 课程名字
* @return 课程价格
*/
public BigDecimal findPriceByName(String courseName){
if (StringUtils.isNotBlank(courseName)){
//从容器中根据serviceName取出对应的实现类
CourseStrategy strategy = (CourseStrategy)applicationContext.getBean(CourseType.getServiceBeanName(courseName));
BigDecimal price = strategy.findPrice();
System.out.println(courseName+"产品一共:"+price+"元");
return price;
}
return null;
}
}
@Test
public void StrategyTest() throws Exception {
courseManager.findPriceByName("English");
courseManager.findPriceByName("Chinese");
courseManager.findPriceByName("Math");
}
English产品一共:100元
Chinese产品一共:300元
Math产品一共:200元
@Service
@DependsOn("courseManager")
public class ChineseService implements CourseStrategy, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
CourseManager.STRATEGY_BEANS.put("Chinese", this);
}
@Override
public BigDecimal findPrice() {
return new BigDecimal(300);//业务查询逻辑,我就举个简单例子了
}
}
@Service
@DependsOn("courseManager")
public class MathService implements CourseStrategy, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
CourseManager.STRATEGY_BEANS.put("Math", this);
}
@Override
public BigDecimal findPrice() {
return new BigDecimal(200);//业务查询逻辑,我就举个简单例子了
}
}
@Service
@DependsOn("courseManager")
public class EnglishService implements CourseStrategy, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
CourseManager.STRATEGY_BEANS.put("English", this);
}
@Override
public BigDecimal findPrice() {
return new BigDecimal(100);//业务查询逻辑,我就举个简单例子了
}
}
然后是服务调用类:
/**
* 课程服务
*/
@Service
public class CourseManager{
public static final Map<String,Object> STRATEGY_BEANS = new ConcurrentHashMap<>();
/**
* 根据课程名字查询价格
* @param courseName 课程名字
* @return 课程价格
*/
public BigDecimal findPriceByName(String courseName){
if (StringUtils.isNotBlank(courseName)){
//从Map中对应的实现类
CourseStrategy strategy = (CourseStrategy)STRATEGY_BEANS.get(courseName);
if (strategy != null){
BigDecimal price = strategy.findPrice();
System.out.println(courseName+"产品一共:"+price+"元");
return price;
}
}
return null;
}
}
测试结果:
English产品一共:100元
Chinese产品一共:300元
Math产品一共:200元
值得注意的是InitializingBean
这个接口的afterPropertiesSet
方法,初始化Bean就会调用这个方法,这里直接将bean放入Map中,引用地址就是IOC中的Bean地址,这样修改后也达到了策略的方式。相比较于最初的服务,现在不管新增多少不同产品的实现,只要实现策略接口重写其方法,将其Bean放入Map中即可,也符合了服务控制类的开闭原则。
@DependsOn("")
此注解的目的是让spring容器按照顺序加载Bean,此注解具体细节可自行百度。