策略模式介绍及其具体使用场景


前言

今天用策略模式优化了下之前的业务代码,重新温习了下设计模式的相关理念,在此记录一下


一、策略模式介绍

定义 :

策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。 (摘自<大话设计模式>)

个人理解:

策略模式简单理解,应该是对于同一个业务功能,在不同的场景需求下提供不同的实现逻辑,来达到动态切换业务算法,满足不同场景的目的。同时它也有另外的好处,即优化代码结构,使其脱离大量逻辑判断,对外只提供 Context上下文,让算法与实际业务代码解耦,对使用者屏蔽底层实现逻辑。

策略模式 UML类图如下
在这里插入图片描述

二、具体使用场景

  • 1、业务代码需要根据场景不同,切换不同的实现逻辑
  • 2、代码中存在大量 if else 逻辑判断

1、举例

这边以我业务代码场景为例,不透露具体代码,举一个相似例子,如下:

        Integer a = 1;
        if (a == 1) {
            // 业务逻辑处理...
        } else if (a == 2) {
            // 业务逻辑处理...
        } else if (a == 3) {
            // 业务逻辑处理...
        }  else if (a == 4) {
            // 业务逻辑处理...
        } else if (a == 5) {
            // 业务逻辑处理...
        } else if (a == 6) {
            // 业务逻辑处理...
        } else if (a == 7) {
            // 业务逻辑处理...
        } else {
            // 业务逻辑处理...
        }

在分支较少,只有两三个的时候,采用上述处理也并无不妥,但是一旦分支多了呢?下次再次新增一个实现方案呢?
这时候代码是不是就变得很囊肿了,让人看着头皮发麻。

2、传统方式优化

分析上述代码,发现可以存在几个优化点:

  1. if else 分支过多
  2. 逻辑处理算法与业务代码耦合
  3. 新增逻辑实现无法优雅扩展

按介绍里 策略模式的UML类图,传统的处理方式是把每一个逻辑处理都封装成一个类,再通过暴露 策略上下文 Context类给调用方使用。
这种方式有个弊端,就是造成的类代码过多,每一种策略都要新增一个实现类,当代码量过于庞大后,后来者也不易读懂。还有就是if else 的分支问题,传统方式处理后,只是把 if else 逻辑判断迁移到了Context 上下文里面,还是避免不了分支过多问题。

传统方式的优化代码,这里不过多介绍

3、Map + 函数式编程 优化

把所有的策略,存到了一个 Map里,通过对key来获取对应的实现方式来执行。value存的是lambda函数的形式。

优化后主要有两个类、
一个是Context 上下文,暴露给客户端调用,这里维护所有的策略。
一个是StrategyImpl ,里面提供所有的策略实际实现,一个策略就是一个方法。

Context上下文代码示例如下

/**
 * 策略上下文.
 *
 * @author linzp
 * @date 2021/11/12 11:14
 */
@Slf4j
@Component
public class XxxxStrategyContext {

    /**
     * 存放所有策略.
     */
    private Map<String, BiFunction<String, String, String>> strategyMap = new HashMap<>(16);

    /**
     * 具体的策略细节.
     */
    @Autowired
    private XxxxStrategyImpl xxxStrategy;

    /**
     * 加载所有策略.
     */
    @PostConstruct
    public void initStrategies() {
        strategyMap.put("one", (arg1, arg2) -> xxxStrategy.doSomethingOne(arg1, arg2));
        strategyMap.put("two", (arg1, arg2) -> xxxStrategy.doSomethingTwo(arg1, arg2));
        strategyMap.put("three", (arg1, arg2) -> xxxStrategy.doSomethingThree(arg1, arg2));
        strategyMap.put("four", (arg1, arg2) -> xxxStrategy.doSomethingFour(arg1, arg2));
        strategyMap.put("five", (arg1, arg2) -> xxxStrategy.doSomethingFive(arg1, arg2));
    }

    /**
     * 指派具体的策略去执行.
     *
     * @param arg1
     * @param arg2
     * @param key 根据key 来获取不同的策略,即上述的 one、two、three...
     * @return
     */
    public String doSomethingByStrategy(String arg1, String arg2, String key) {
        BiFunction<String, String, String> biFunction = strategyMap.get(key);
        if (Objects.isNull(biFunction)) {
            // 没有找到特定的策略,则采用默认实现(一般只有加了新的类型,但是没有配置对应的处理策略才会走到这里)
            log.warn("========= 没有配置该处理策略,采用默认实现,key {} ====== " + key);
            return xxxStrategy.doSomethingDefault(arg1, arg2);
        }
        return biFunction.apply(arg1, arg2);
    }
}

具体实现策略, XxxxStrategyImpl 类代码如下:


/**
 * 所有策略具体实现.
 *
 * @author linzp
 * @date 2021/11/12 14:57
 */
@Component
public class XxxxStrategyImpl {

    /**
     * 默认实现
     *
     * @param arg1
     * @param arg2
     * @return
     */
    public String doSomethingDefault(arg1, arg2){
    	// 默认业务逻辑处理...
        return "";
    }

    /**
     * 策略1
     *
     * @param arg1
     * @param arg2
     * @return
     */
    public String doSomethingOne(arg1, arg2){
    	// 业务逻辑处理 1...
        return "";
    }
    
    /**
     * 策略2
     *
     * @param arg1
     * @param arg2
     * @return
     */
    public String doSomethingTwo(arg1, arg2){
    	// 业务逻辑处理 2...
        return "";
    }

    /**
     * 策略3
     *
     * @param arg1
     * @param arg2
     * @return
     */
    public String doSomethingThree(arg1, arg2){
    	// 业务逻辑处理 3...
        return "";
    }
	
	// 省略后续策略代码 ....
}

如此,便可消除所有分支判断,也仅用两个类,就完成了传统方式的策略实现。

总结

好的代码设计,可以让后续在面对不断变化的需求时,不必手忙脚乱,加班加点,也给后人的接手留一条活路 (狗头)。

码字不易~ ~ 如果帮到了你,点个赞啪~~

  • 7
    点赞
  • 8
    收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页
评论 7

打赏作者

林志鹏JAVA

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值