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,如果确定具体的算法不会变,一两个这种,没有必要滥用设计模式,代码是一步一步优化出来的,不是一步到位实现的。大于三个可以使用卫语句,考虑策略模式来简化代码。