一、什么是策略模式
所谓策略模式,就是定义了一组策略,分别封装在不同类中,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如我们要去某个地方,会根据距离的不同来选择不同的出行方式,比如:共享单车、打车、坐飞机等等,这些出行方式即不同的策略。
二、为什么使用策略模式
我们在平时为了快速完成业务需求,会在代码实现时大量使用if…else,但当代码中大量充实着这种结构时会使代码的可读性、维护性降低并更容易出错。策略模式是解决这种问题的好办法,它符合设计模式中的开闭原则。
三、策略模式的组成
3.1 结构
3.2 角色
- Context(环境类)
策略上下文,维护一个Strategy对象的引用,屏蔽上层模块对策略、算法的直接访问 - Strategy(抽象策略类)
这是一个抽象角色,定义了所有的具体策略类所需的接口 - ConcreteStrategy(具体策略类)
实现了在抽象策略类中定义的方法,封装了相关的算法或行为。根据多态的特性,在运行时,具体策略类将覆盖在环境类中定义的抽象策略累对象
四、如何实现策略模式
源代码:https://github.com/mofan3/strategy-mode.git
4.1 基于注解实现
- 枚举定义
/**
* 消息类型枚举
*/
@Getter
@AllArgsConstructor
public enum MessageType {
PUSH("通知栏"),
SMS("短信"),
EMAIL( "邮件"),
WECHAT( "微信"),
DING_DING_ROBOT( "钉钉机器人"),
DING_DING_WORK_NOTICE("钉钉工作通知")
;
/**
* 描述
*/
private String description;
}
- 注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface HandlerAnnotation {
MessageType type();
}
- 策略接口定义
public interface BizHandler {
void execute(MessageInfo messageInfo);
}
- 策略实现
@HandlerAnnotation(type = MessageType.SMS)
@Slf4j
public class SmsHandler implements BizHandler {
@Override
public void execute(MessageInfo messageInfo) {
SmsMessageInfo smsMessageInfo = (SmsMessageInfo) messageInfo;
// 发送短信
log.info("发送短信消息:{}",smsMessageInfo.getContent());
}
}
@HandlerAnnotation(type = MessageType.WECHAT)
@Slf4j
public class WeChatHandler implements BizHandler {
@Override
public void execute(MessageInfo messageInfo) {
WeChatMessageInfo weChatMessageInfo = (WeChatMessageInfo) messageInfo;
// 发送微信
log.info("发送微信消息:{}",weChatMessageInfo.getContent());
}
}
- 策略组装
@Component
public class BizHandlerContext implements BeanPostProcessor {
private final Map<String, BizHandler> bizHandlerMap = new HashMap<>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
HandlerAnnotation annotation = AnnotationUtils.findAnnotation(bean.getClass(), HandlerAnnotation.class);
if (ClassUtils.isAssignableValue(BizHandler.class,bean) && annotation != null) {
bizHandlerMap.put(annotation.type().name(), (BizHandler)bean);
}
return bean;
}
public BizHandler getBizHanler(String type) {
BizHandler bizHandler = this.bizHandlerMap.get(type);
if (bizHandler == null) {
throw new RuntimeException("not.found.BizHandler");
}
return bizHandler;
}
}
BizHandlerContext类实现BeanPostProcessor接口,在postProcessBeforeInitialization(…)方法中拿到所有含有@HandlerAnnotation注解的bean,以bean的注解中的type值作为键和bean作为值构建handlerMap,从而在外界以type作为请求时可以准确的获取到handlerMap中相应的bean。
- 测试
@SpringBootTest
class StrategyModeApplicationTests {
@Autowired
BizHandlerContext bizHandlerContext;
@Test
void contextLoads() {
AbstractBizHandler bizHanler = bizHandlerContext.getBizHanler(MessageType.SMS.name());
SmsMessageInfo smsContentModel = new SmsMessageInfo();
smsContentModel.setContent("【XXX】验证码:313122,您正在进行登录操作,若非本人操作,请勿泄露");
bizHanler.execute(smsContentModel);
}
}
4.2 基于@Autowired的实现
- 策略接口定义
public interface BizHandler {
void execute(MessageInfo messageInfo);
String getType();
}
- 策略实现
@Service
@Slf4j
public class SmsHandler implements BizHandler {
@Override
public void execute(MessageInfo messageInfo) {
SmsMessageInfo smsMessageInfo = (SmsMessageInfo) messageInfo;
// 发送短信
log.info("发送短信消息:{}",smsMessageInfo.getContent());
}
@Override
public String getType() {
return MessageType.SMS.name();
}
}
@Service
@Slf4j
public class WeChatHandler implements BizHandler {
@Override
public void execute(MessageInfo messageInfo) {
WeChatMessageInfo weChatMessageInfo = (WeChatMessageInfo) messageInfo;
// 发送微信
log.info("发送微信消息:{}",weChatMessageInfo.getContent());
}
@Override
public String getType() {
return MessageType.WECHAT.name();
}
}
- 策略组装
@Component
public class BizHandlerContext{
private final Map<String, BizHandler> bizHandlerMap = new HashMap<>();
@Autowired(required = false)
public void putBizHandler(List<BizHandler> bizHandlerList) {
for (BizHandler bizHandler : bizHandlerList) {
String type = bizHandler.getType();
if (StringUtils.isEmpty(type)) {
throw new IllegalArgumentException("BizHandler name must not be empty");
}
bizHandlerMap.put(type, bizHandler);
}
}
public BizHandler getBizHanler(String type) {
BizHandler bizHandler = this.bizHandlerMap.get(type);
if (bizHandler == null) {
throw new RuntimeException("not.found.BizHandler");
}
return bizHandler;
}
}
BizHandlerContext使用了Spring的@Autowired注解按照类型自动装配的功能,循环遍历BizHandler集合,获取type值作为键和bean作为值加入到handlerMap中。
- 测试
@SpringBootTest
class StrategyModeApplicationTests {
@Autowired
BizHandlerContext bizHandlerContext;
@Test
void contextLoads() {
AbstractBizHandler bizHanler = bizHandlerContext.getBizHanler(MessageType.SMS.name());
SmsMessageInfo smsContentModel = new SmsMessageInfo();
smsContentModel.setContent("【XXX】验证码:313122,您正在进行登录操作,若非本人操作,请勿泄露");
bizHanler.execute(smsContentModel);
}
}
4.3 基于InitializingBean的实现
- 抽象处理器定义
public abstract class AbstractBizHandler implements InitializingBean {
@Autowired
private BizHandlerContext bizHandlerContext;
@Override
public void afterPropertiesSet() throws Exception {
bizHandlerContext.putBizHandler(getType(),this);
}
public abstract void execute(MessageInfo messageInfo);
public abstract String getType();
}
InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法。也可用@PostConstruct注解实现,但接口实现方式在调用效率上高一些,@PostConstruct是通过反射机制调用的
- 策略实现
@Service
@Slf4j
public class SmsHandler extends AbstractBizHandler {
@Override
public void execute(MessageInfo messageInfo) {
SmsMessageInfo smsMessageInfo = (SmsMessageInfo) messageInfo;
// 发送短信
log.info("发送短信消息:{}",smsMessageInfo.getContent());
}
@Override
public String getType() {
return MessageType.SMS.name();
}
}
@Service
@Slf4j
public class WeChatHandler extends AbstractBizHandler {
@Override
public void execute(MessageInfo messageInfo) {
WeChatMessageInfo weChatMessageInfo = (WeChatMessageInfo) messageInfo;
// 发送微信
log.info("发送微信消息:{}",weChatMessageInfo.getContent());
}
@Override
public String getType() {
return MessageType.WECHAT.name();
}
}
- 策略组装
@Component
public class BizHandlerContext{
private final Map<String, AbstractBizHandler> bizHandlerMap = new HashMap<>();
public void putBizHandler(String type, AbstractBizHandler handler) {
bizHandlerMap.put(type, handler);
}
public AbstractBizHandler getBizHanler(String type) {
AbstractBizHandler bizHandler = this.bizHandlerMap.get(type);
if (bizHandler == null) {
throw new RuntimeException("not.found.BizHandler");
}
return bizHandler;
}
}
- 测试
@SpringBootTest
class StrategyModeApplicationTests {
@Autowired
BizHandlerContext bizHandlerContext;
@Test
void contextLoads() {
AbstractBizHandler bizHanler = bizHandlerContext.getBizHanler(MessageType.SMS.name());
SmsMessageInfo smsContentModel = new SmsMessageInfo();
smsContentModel.setContent("【XXX】验证码:313122,您正在进行登录操作,若非本人操作,请勿泄露");
bizHanler.execute(smsContentModel);
}
}
五、总结
通过策略设计模式的使⽤可以把我们方法中的if语句优化掉,以上3种实现方式本质上是利用Map的key-value结构做映射来解决⼤量if else 的叠加嵌套问题,⼤量的if语句使用会让代码难以扩展,也不好维护,同时在后期遇到各种问题也很难维护。在使用这样的设计模式后可以很好的满⾜隔离性与和扩展性,对于不断新增的需求也⾮常⽅便承接。