前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于SpringBoot的策略模式demo

基于SpringBoot的策略模式demo

作者头像
关忆北.
发布2020-12-01 10:28:39
9250
发布2020-12-01 10:28:39
举报
文章被收录于专栏:关忆北.关忆北.
策略模式

策略模式是Java 23种设计模式之一,在https://refactoring.guru/design-patterns/网站中这样对策略模式进行了解释:

代码语言:javascript
复制
Strategy is a behavioral design pattern that turns a set of behaviors into objects and makes them interchangeable inside original context object.

白话翻译一下就是:策略模式可以根据上下文对象的不同状态去执行不同的逻辑(策略实现)。最简单使用场景是:当代码中出现了三重以上的if else判断,这时代码的可读性会非常差,这时可以使用到策略模式去拯救if else.当然也可以使用switch,但是相较于策略模式,switch的代码清晰度还是差了些。

策略模式不同角色
  1. Strategy:抽象策略角色,对算法、策略的抽象,定义每个算法、策略所必需的方法,通常为接口。
  2. ConcreteStrategy:具体策略角色,实现抽象策略角色,完成具体的算法、策略。
  3. Context:上下文环境角色,保存了ConcreteStrategy,负责调用ConcreteStrategy。
SpringBoot下使用策略模式

加减乘除的计算器如果使用传统的if else的话,至少需要写三次if和一次else,如果使用switch的话,则在一个方法中堆叠四个case或者三个case一个default,如果每个if条件下的逻辑都特别多的话,代码会显得特别臃肿,在《阿里巴巴开发手册》中规定:一个方法中代码量不能超过80行,如果逻辑判断都挤在一个方法里,代码量一定会超过80行,ok,你的KPI完了。所以,年轻人,耗子尾汁,还不赶紧学学策略模式。

接下来我将使用加减乘除的一个小案例来写一个最简单的策略模式的demo

  • 0、引入pom
代码语言:javascript
复制
       <!-- SpringBoot所必须的依赖-->
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
        </dependency>
    
    <!-- demo中使用guava的Maps集合和判空-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>29.0-jre</version>
        </dependency>

建议:新建一个strategy包,包下定义策略接口实现包strategyImpl和不同的策略角色,也就是上文中的2 ps:实际项目中传参可能不是基本类型,而是一个自定义的一个包装类型(实体类),所以入参判断可以使用@Validated注解判空,我这里是int基本类型,所以在判空的时候就使用了Google guava封装的Preconditions去判空,业务判空一定要提前,不要走到较深的业务中再去判空

1、定义策略接口,也就是上文中的1

CalculationStrategy.java

代码语言:javascript
复制
/**
 * @author Liu-PC
 */
public interface CalculationStrategy {
    /**
     * 策略模式的策略接口定义
     * @param num1 first
     * @param num2 second
     * @return
     */
    int operate(int num1, int num2);

}

AddCalculationStrategyImpl.java

代码语言:javascript
复制
/**
 * @author Liutx
 * @date 2020/11/28 16:05
 * @Description
 */
@Component("add")
public class AddCalculationStrategyImpl implements CalculationStrategy {
    @Override
    public int operate(int num1, int num2) {
        return num1 + num2;
    }
}

这里的@Component是把当前策略的实现类注册为一个组件,交由Spring管理。add是策略的名字,策略的名字不能重复,因为我们策略接口通过组件的名字来找到具体的策略角色。

DivisionStrategyImpl.java

代码语言:javascript
复制
/**
 * @author Liutx
 * @date 2020/11/28 16:18
 * @Description
 */
 @Component("Division")
public class DivisionStrategyImpl implements CalculationStrategy {
    @Override
    public int operate(int num1, int num2) {
        return num1 / num2;
    }
}

MultiplicationStrategyImpl.java

代码语言:javascript
复制
@Component("multiple")
public class MultiplicationStrategyImpl implements CalculationStrategy {
    @Override
    public int operate(int num1, int num2) {
        return num1 * num2;
    }
}

SubtractionStrategyImpl.java

代码语言:javascript
复制
@Component("subtract")
public class SubtractionStrategyImpl implements CalculationStrategy {

    @Override
    public int operate(int num1, int num2) {
        return num1 - num2;
    }
}
  • 3、创建上下文对象去调用策略

CalculationContext.java

代码语言:javascript
复制
/**
 * @author Liutx
 * @date 2020/11/28 16:23
 * @Description 策略上下文类,把传入的参数放到map中,作为策略接口的入参
 */

@Component
@Getter
public class CalculationContext {
    /**
     *  把策略角色(类型)key,和参数value放到Map中
     *  key就是beanName(具体策略实现类中@Component的名字),value就是接口(具体的实现类)
     *  Maps是guava下的封装类型,实则是静态的创建了一个HashMap的对象,Maps可以根据key去获取value对象
     */

    public final Map<String, CalculationStrategy> calculationStrategyMap = Maps.newHashMapWithExpectedSize(4);

    public CalculationContext(Map<String, CalculationStrategy> calculationStrategyMap) {
        this.calculationStrategyMap.clear();
        this.calculationStrategyMap.putAll(calculationStrategyMap);
    }
    
    
    //可以使用@Getter注解代替,这样写方便读者理解在Service层调用Context执行策略
    public Map<String, CalculationStrategy> getCalculationStrategyMap() {
        return calculationStrategyMap;
    }
}

同样需要使用@Component,个人理解策略模式中的Context对象实例就像是一个执行者

以上,策略模式就已经写完了,下面是Controller中调用Service,Service中的Context"执行者"通过@Autowired注入的方式去获得Context对象,对象根据不同的策略角色去执行不同的策略实现。

CalculationService.java

代码语言:javascript
复制
/**
 * @author Liutx
 * @date 2020/11/28 16:03
 * @Description Service里执行,相当于写在ServiceImpl里的业务逻辑,可以在Controller里调用
 * 上下文环境角色,保存了ConcreteStrategy,负责调用ConcreteStrategy,所以就使用Context对象去执行策略
 * 策略的不同实现类,就相当于if else中不同的逻辑代码
 * 本demo使用加减乘除代替不同的策略逻辑
 */

@Service
public class CalculationService {

    @Autowired
    private CalculationContext calculationContext;

    public int operateByStrategy(String strategy, int num1, int num2) {
        //获取入参,根据不同的参数类型去执行不同的策略,Context的get方法是在这个地方用到的,operate方法就是一开始定义的策略接口
        return calculationContext.getCalculationStrategyMap().get(strategy).operate(num1, num2);
    }
}

TestController.class

代码语言:javascript
复制
@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    private CalculationService calculationService;

    @GetMapping("calculation")
    public int testCalculation(String operation, int num1, int num2) {
        //省略参数判空
        return condition = calculationService.operateByStrategy(operation, num1, num2);
    }
}
Postman测试
在这里插入图片描述
在这里插入图片描述

1-1=0

over ~ ~ ~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-11-29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 策略模式
  • 策略模式不同角色
  • SpringBoot下使用策略模式
  • Postman测试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档