1 业务场景
假设有这样的业务场景,MQ把消息推送过来,根据不同类型采取不同的解析 方式。多数的小伙伴就会写出以下的代码:
f(type=="A"){
//按照A格式解析
}else if(type=="B"){
//按B格式解析
}else{
//按照默认格式解析
}
这个代码可能会存在哪些问题呢 ?
- 如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低 。
- 如果你需要接入一种新的解析类型,那只能在原有代码上修改 。
说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则 以及单一原则 。
- 开闭原则 (对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码
- 单一原则 (规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。
如果你的代码就是酱紫:有多个if...else
等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式 来优化。
2 策略模式定义
策略模式 定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的的客户。这个策略模式的定义是不是有点抽象呢?那我们来看点通俗易懂的比喻:
假设你跟不同性格类型的小姐姐约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去逛街买买买最合适。当然,目的都是为了得到小姐姐的芳心,请看电影、吃小吃、逛街就是不同的策略。
策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
3 策略模式的使用
策略模式怎么使用呢?酱紫实现的:
- 一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法)
- 不同策略的差异化实现(就是说,不同策略的实现类)
- 使用策略模式
3.1 一个接口,两个方法
/**
* 消息上报接口
*
* @author lyl
* @version 2021/11/18 09:15:45
*/
public interface IMessageStrategy {
/**
* 属于哪种消息解析类型
*
* @return .
*/
MessageTypeOperationEnum gainMessageType();
/**
* 业务参数方法
*
* @param response 消息参数
*/
void operation(Object objectParam);
}
3.2 定义消息类型
/**
* 消息通知类型
*
* @author lyl
* @version 2021/11/18 09:21:57
*/
@Getter
@AllArgsConstructor
public enum MessageTypeOperationEnum {
//消息通知类型
ALARM("1", "告警上报"),
FAULT("2", "故障修复上报"),
HEART_BEAT("3", "心跳上报");
private String code;
private String message;
public static MessageTypeOperationEnum match(String key) {
MessageTypeOperationEnum result = null;
for (MessageTypeOperationEnum s : values()) {
if (key.equals(s.getCode())) {
result = s;
break;
}
}
return result;
}
public static MessageTypeOperationEnum catchMessage(String msg) {
MessageTypeOperationEnum result = null;
for (MessageTypeOperationEnum s : values()) {
if (s.getMessage().equals(msg)) {
result = s;
break;
}
}
return result;
}
}
3.3 策略的差异化实现
A 类型消息策略具体实现
@Slf4j
@Component
@AllArgsConstructor
public class HeartBeatOperation implements IMessageStrategy {
@Override
public MessageTypeOperationEnum gainMessageType() {
return MessageTypeOperationEnum.HEART_BEAT;
}
@Override
public void operation(Object objectParam) {
logger.info("A 类型消息解析,参数:{}",objectParam);
//心跳消息的具体处理业务
}
}
B 类型消息策略具体实现
@Slf4j
@Component
@AllArgsConstructor
public class AlarmOperation implements IMessageStrategy {
@Override
public MessageTypeOperationEnum gainMessageType() {
return MessageTypeOperationEnum.HEART_BEAT;
}
@Override
public void operation(Object objectParam) {
logger.info("B 类型消息解析,参数:{}",objectParam);
//告警消息的具体处理业务
}
}
C 类型消息策略具体实现
@Slf4j
@Component
@AllArgsConstructor
public class FaultOperation implements IMessageStrategy {
@Override
public MessageTypeOperationEnum gainMessageType() {
return MessageTypeOperationEnum.HEART_BEAT;
}
@Override
public void operation(Object objectParam) {
logger.info("C 类型消息解析,参数:{}",objectParam);
//故障上报消息的具体处理业务
}
}
3.4 使用策略模式
/**
* 消息策略封装
*
* @author lyl
* @version 2021/11/18 10:42:54
*/
@Component
public class StrategyMessageService implements ApplicationContextAware {
private Map<MessageTypeOperationEnum, IMessageStrategy> iMessageStrategyMap = new ConcurrentHashMap<>();
/**
* 使用消息策略,改方提供给需要使用测试的业务调用,然后会改方法会根据具体的消息类型,调用具体策略实现
*
* @param messageTypeOperationEnum 消息类型
* @param queueResponse 消息参数
*/
public void operation(MessageTypeOperationEnum messageTypeOperationEnum, OuterSouthRestResponse<OuterResponseSouthData> queueResponse) {
IMessageStrategy iMessageStrategy = iMessageStrategyMap.get(messageTypeOperationEnum);
if (iMessageStrategy != null) {
//使用策略模式调用,这样就走具体策略方法
iMessageStrategy.operation(queueResponse);
}
}
/**
* 策略封装
*
* @param applicationContext .
* @throws BeansException .
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IMessageStrategy> tempMap = applicationContext.getBeansOfType(IMessageStrategy.class);
tempMap.values().forEach(strategyService -> iMessageStrategyMap.put(strategyService.gainMessageType(), strategyService));
}
}
3.5 具体的业务调用
/**
* 消息监听业务
*
* @author lyl
* @version 2021/4/5 0005 11:45:10
*/
@Slf4j
@Component
public class MessageListener {
@Autowired
private StrategyMessageService strategyMessageService;
/**
* 告警上报
*
* @param message 。
* @param channel 。
* @throws IOException 。
*/
@RabbitListener(queues = MqConstant.REPORT)
public void alarmQueues(Message message, Channel channel) throws IOException {
strategyMessageService.operation(MessageTypeOperationEnum.ALARM,message)
}
/**
* 故障修复
*
* @param message 。
* @param channel 。
* @throws IOException 。
*/
@RabbitListener(queues = MqConstant.OPEN)
public void faultQueues(Message message, Channel channel) throws IOException {
strategyMessageService.operation(MessageTypeOperationEnum.FAULT,message)
}
/**
* 心跳上报
*
* @param message 。
* @param channel 。
* @throws IOException 。
*/
@RabbitListener(queues = MqConstant.HEARTBEAT)
public void heartBeatQueues(Message message, Channel channel) throws IOException {
strategyMessageService.operation(MessageTypeOperationEnum.HEART_BEAT,message)
}
}
如果在操作中遇到问题可以通过公众号留言,希望能能够帮住你解决。