策略模式【结合Spring框架实现】

Hello!~大家好啊,很高兴我们又见面了,今天我们一起学习设计模式--【策略模式】

初次对此模式不懂的,或者想偷懒的,我强烈建议大家跟着我的一起把概念和代码一起敲一遍!~为啥子??因为我就是这样学会的,哈哈哈!

1.首先我们看下此模式的整体UML

selector:选择器又叫 做上下文context,【作用】通过不同的标识从而获取对应的策略实例(这是很关键的一个点,你细品~)

抽象策略: 如图所示,这是策略模式的顶层接口【就相当于领导~干运筹帷幄的】

策略实例 n:封装不同算法业务的实例对象,【就是特么真正干活的~!比如俺们码农】

2.案例实现

我们一个实际的案例进行实操,掌握这看起来高大上的策略模式,也是后面若你要面试,也能吹牛逼啊~

案例:

比如我们要统计餐厅的人流量数据做统计,统计的时间维度:一周和一个月,我们用策略模式咋实现呢? 我们往下看

实现需求分析:

  1. 前置设计:
    1. 我们定义常量标识策略的类型,
    2. 使用者调用时可通过常量获取对应的策略实例
  2. 策略设计
    1. 策略实例: 创建两个Bean实例: 周维度 &&月维度,并实现其中对应的算法和业务
    2. 抽象策略:通常我们定义一个通用的接口 或者抽象方法 ,为啥???为了让外部能够访问啊!否则不是闭关锁国了嘛!~~
  3. 选择器设计: 这块就是挺重要的啦,我们通常采用MAP来存储数据,这样调用者就可以通过策略标识获取对应的策略实例

注意:

对于第三点可以是一道面试题:请问你怎么将策略的Bean注入到MAP中的?(不看答案,别心里好家伙,思考思考~哈哈)

也许你的第一反应:

我就直接将Bean对应的Class写死在``MAP中,然后通过applicationContext.getBaen()`获取其实例,这是最简单粗暴的!~(也一看就是菜鸡写法~哈哈哈)

若是这样回答,面试官肯定会问,这个通过写死的方法,那么下次我想增加其他维度的那不是要再次往Map中再添加,这麻烦先不说,也不符合开闭原则啊!~

你尴尬的扣了扣脚,微笑回答到:

是的,这是我一开始使用的,当时业务量不大,只考虑上面的两种情况,我就直接使用采取这种直接写死法,

但后面随着业务线的拓展,我也发现此中写法的弊端:

我的改进措施 :(在Springboot的环境中)

1.通过解析注解来实现,给每个策略实例打上一个注解,我们业务是以周和月为维度进行常量标识的

2.过程: 在Springboot启动时,通过扩展点扫描抽象策略,获取它的策略实例

3.然后解析注解,放入MAP

这种就不用对原先代码就行逻辑代码改动,既符合开闭原则,又回到了面试官,爽哉!~

说了这么多,那关于代码我们怎么实现呢?-->耐心点,我们一起往下看

代码实现

1.抽象策略

java 复制代码
package org.boyunv.strategy_pattern.handler;

/**
 * <p>
 * 描述:  抽象策略    <br>
 * <p>
 * 需求信息: 【需求ID与需求标题】【客户名称】 <br>
 *
 * @author aristo
 * @date 2024/1/23 19:07
 */
public interface TimeDimensionBaseHandlerInterface {
    //此方法是留给不同策略实体实现不同策略的方法实现
    void  statistic();
}

2.【策略实体】

这里我们注册组件直接采用Spring框架的组件@Component进行注册即可
1.周统计维度

java 复制代码
package org.boyunv.strategy_pattern.handler;

import org.springframework.stereotype.Component;

/**
 * <p>
 * 描述:  策略实体: 周统计维度$      <br>
 * <p>
 * 需求信息: 【需求ID与需求标题】【客户名称】 <br>
 *
 * @author aristo
 * @date 2024/1/23 19:12
 */
@Component("week")
public class StatisticByWeekHandler implements TimeDimensionBaseHandlerInterface{
    @Override
    public void statistic() {
        //具体实现
        System.out.println("通过周统计维度实现统计数据");
    }
}

2.月统计维度

java 复制代码
package org.boyunv.strategy_pattern.handler;

import org.springframework.stereotype.Component;

/**
 * <p>
 * 描述:  策略实体: 月统计维度$      <br>
 * <p>
 * 需求信息: 【需求ID与需求标题】【客户名称】 <br>
 *
 * @author aristo
 * @date 2024/1/23 19:12
 */
@Component("month")
public class StatisticByMonthHandler implements TimeDimensionBaseHandlerInterface{

    @Override
    public void statistic() {
        System.out.println("通过月统计维度实现统计数据");
    }
}

3.选择器【Inevitable!】

看如下的代码,我们会发现他同样是个组件,@Component

@Resource:将我们注册的weekmonth组件注册进入MAP

java 复制代码
package org.boyunv.strategy_pattern.selector;

import org.boyunv.strategy_pattern.handler.TimeDimensionBaseHandlerInterface;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 描述:  选择器,核心重点$      <br>
 * 需求信息: 【需求ID与需求标题】【客户名称】 <br>
 * @date 2024/1/23 19:26
 */
@Component
public class StatisticSelector {
    //这就和我们上面的所说的:定义Map,并通过@Resource注解完成我们刚才week和month的组件注入(很关键的一步哦!~)
    @Resource
    private Map<String, TimeDimensionBaseHandlerInterface> selectorMap;

    //下面根据类型选择策略
    public TimeDimensionBaseHandlerInterface select(String type){
        return selectorMap.get(type);
    }
}

4.我们进行测试

java 复制代码
package org.boyunv.strategy_pattern.selector;

import org.boyunv.strategy_pattern.handler.TimeDimensionBaseHandlerInterface;
import org.junit.Test;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 描述:  选择器,核心重点$      <br>
 * 需求信息: 【需求ID与需求标题】【客户名称】 <br>
 * @date 2024/1/23 19:26
 */
@Component
public class StatisticSelector {
    //这就和我们上面的所说的:定义Map,并通过@Resource注解完成我们刚才week和month的组件注入(很关键的一步哦!~)
    @Resource
    private Map<String, TimeDimensionBaseHandlerInterface> selectorMap;

    //下面根据类型选择策略
    public TimeDimensionBaseHandlerInterface select(String type){
        return selectorMap.get(type);
    }

    public  void doInvoke(String type){
        TimeDimensionBaseHandlerInterface handler=select(type);
        handler.doStatistic();
    }
    @Test
    public void invoke(){
        this.doInvoke("week");
        this.doInvoke("month");
    }
}

完结:

通过上面的运行步骤,我们最中实现策略模式的周和月的策略实现

这里面的核心点,再次提醒下:

通过Spring获取接口的实现,并解析实现类上的注解的方式,可以在程序启动时动态的将策略注入到一个Map中,作为策略的容器。

使用时传入标识符(就是常量)就可以获取到对应的策略执行了。

相关推荐
import_random35 分钟前
[macos]rocketmq(安装)
后端
程序员小假1 小时前
你会不会使用 SpringBoot 整合 Flowable 快速实现工作流呢?
java·后端
明月与玄武1 小时前
快速掌握Django框架设计思想(图解版)
后端·python·django
陪我一起学编程1 小时前
关于ORM增删改查的总结——跨表
数据库·后端·python·django·restful
南囝coding2 小时前
这个 361K Star 的项目,一定要收藏!
前端·后端·github
虎鲸不是鱼2 小时前
Spring Boot3流式访问Dify聊天助手接口
java·spring boot·后端·大模型·llm
onlooker66662 小时前
Go语言底层(五): 深入浅出Go语言的ants协程池
开发语言·后端·golang
武子康2 小时前
Java-46 深入浅出 Tomcat 核心架构 Catalina 容器全解析 启动流程 线程机制
java·开发语言·spring boot·后端·spring·架构·tomcat
寻月隐君3 小时前
Solana 开发实战:Rust 客户端调用链上程序全流程
后端·rust·web3
哆啦A梦的口袋呀3 小时前
基于Python学习《Head First设计模式》第十一章 代理模式
学习·设计模式·代理模式