Spring项目中策略模式使用

一、策略模式定义

策略模式(Strategy Pattern)是行为型策略模式当中的一种,定义一系列算法,将每个算法封装到具有公共接口的一系列策略类中,(可以是抽象类,也可以是接口),从而使他们可以相互替换,让算法可在不影响客户端的情况下发生变化,
作用:
将算法的责任和本身进行解耦,使得:
1、算法可独立于使用外部而变化
2、客户端方便根据外部条件选择不同的策略来解决不同的问题
定义策略接口:

public interface EventStrategy<T extends Message> {

    /**
     * 处理事件消息
     * @param Message
     * @return
     */
     boolean handleMessage(T Message);
}

二、实现

具体策略实现类:

UML图如下:
这里比如说有一个需要发送消息通知的队列,可能需要发到微信,也可能需要发到飞书,各个平台的api不一样,所以单独采用一个策略实现

像之前的项目注册到策略都采用new 对象的方式,这里因为是spring项目,依赖于DI,所以自动注入到Context当中,不需要我们手动操作

@Slf4j
@Service
public class EventContext {

    private static Map<String, EventStrategy> STRATEGY_MAP = new HashMap<>(16);

    public EventContext(List<EventStrategy> messageServices) {
        messageServices.forEach(service ->
                service.getMessageTypes().forEach(type ->  STRATEGY_MAP.put((String) type, service)));
    }

    public boolean consumeEventMessage(Message message) {
        STRATEGY_MAP.get(message.getType()).handleMessage(message);
        return true;
    }
}

注入到STRATEGY_MAP的方法很多,如果是非spring的项目可能需要将每一个实现类手动添加进去

这里使用的是构造器注入
也可以实现ApplicationContextAware

public class EventContext implements ApplicationContextAware {

    private static Map<String, EventStrategy> STRATEGY_MAP = new HashMap<>(16);

    public boolean consumeEventMessage(Message message) {
        STRATEGY_MAP.get(message.getType()).handleMessage(message);
        return true;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, EventStrategy> beanMap = applicationContext.getBeansOfType(EventStrategy.class);
        beanMap.forEach((name, service) -> {
            STRATEGY_MAP.put(service.getMessageType(), service);
        });
    }
}

当然也可以实现InitializingBean

这里使用构造器注入更简洁,也符合spring的设计理念

三、扩展:

策略类map中的key也可以定义为bean的名字,只需要设置@Service注解当中的value属性,注入map的时候也将bean的name作为key值,但是这样不利于扩展
这里假如可能有两种平台的消息通知是一样的,消息type是163mali和QQmail都属于邮件类型消息通知,当它们共用一个策略时,可以在接口定义抽象方法,全部注册到STRATEGY_MAP
举例说明:


可以看到成功注入进去,
这里策略上下文的key值一般是由枚举定义,

修改接口方法的类型与实现类

最后修改如下

这里上下文可能更多的是返回具体的实现类,来实现不同的逻辑

新建测试类,可以看到最终效果

四、总结

优点:
1、依赖Spring的DI,简化了手动创建策略类实例的过程
2、调用方无需知道具体的策略实现逻辑,可以干掉多重if else, 降低模块间的耦合
3、方便扩展,如果有新的类型,只需要实现接口类并交注入到spring交给容器处理即可,符合开闭原则
缺点:
1、使用不当可能会造成策略实现类臃肿
如果条件不是很多,尽量使用if else,如果确定具体的算法不会变,一两个这种,没有必要滥用设计模式,代码是一步一步优化出来的,不是一步到位实现的。大于三个可以使用卫语句,考虑策略模式来简化代码。

github地址
https://github.com/LiuFqiang/redis-case/tree/main/src/main/java/org/pigliu/rediscase/service/strategy

posted @ 2023-07-17 18:26  木马不是马  阅读(1934)  评论(0编辑  收藏  举报