您的位置:首页 > 编程语言

设计模式之策略模式(if..else肿瘤代码)

2020-06-02 05:50 323 查看

你是否有过类似如下的业务代码,每种业务类型都有不同的处理数据,但是要返回的结果功能是一样的:

/**
* 根据课程名字查询价格
* @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容器因地制宜地解决此问题。

1.创建策略接口

/**
* 课程策略接口
*/
public interface CourseStrategy {

/**
* 根据课程名字查询价格
* @param courseName 课程名字
* @return 课程价格
*/
BigDecimal findPriceByName(String courseName);

}

2.每种产品服务类实现该接口

  • 不同service实现接口并根据不同的产品需求重写策略方法
@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);//业务查询逻辑,我就举个简单例子了
}
}

3.枚举类(下文有其它方案)

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;}
}

4.服务调用类

通过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;
}
}

5.测试一下

@Test
public void StrategyTest() throws Exception {
courseManager.findPriceByName("English");
courseManager.findPriceByName("Chinese");
courseManager.findPriceByName("Math");
}
English产品一共:100元
Chinese产品一共:300元
Math产品一共:200元

第三步也算是有一定耦合,可以继续改造:

  • 可以写一个注解,不同的产品service加上此策略注解,然后服务调用类通过反射方式获取service类名,然后通过IOC容器调用。
  • 可以在服务调用类中实例化一个Map,不同产品service实例化后注册进Map中,我们演示一下此方法。
@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,此注解具体细节可自行百度。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: