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

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

    /**
     * 根据课程名字查询价格
     * @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,此注解具体细节可自行百度。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Minor王智

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值