if/else
在日常开发中,基于业务的复杂,需要对不同的场景进行判断,每当多出一种情况,那么又要新增一条if/else,这样让代码看起来非常的冗长以及修改起来很麻烦,刚才看到其中一种思路,应用到了设计模式中的策略模式。
设计模式有20多种包括:单例,适配器、策略、工厂…这里仅仅是其中一种,算是我自己对刚敲的案例的理解和总结吧。我算是萌新吧,如果觉得啰嗦请谅解一下~
需求场景
一个聊天平台,由于双方发送的消息的内容不同所以有不用的处理方式,觉得原来的if/else太过于繁琐且耦合度高,不易扩展,就算换成switch匹配的话后期添加新的类型也会很麻烦,所以想要用策略模式优化。
原场景
if (msgType = "文本") {
// dosomething
} else if(msgType = "图片") {
// doshomething
} else if(msgType = "视频") {
// doshomething
} else {
// doshomething
}
消息对象
@Data // lombok插件 自动生成set/get方法以及equals等 可以简化开发 挺好用的
@AllArgsConstructor
public class MessageInfo {
/**
* 消息类型
*/
private Integer messageCode;
/**
* 内容
*/
private String content;
}
消息类型枚举类
public enum MSG_TYPE {
TEXT(1, "文本"),
IMAGE(2, "图片"),
VIDEO(3, "视频");
public final int code;
public final String name;
MSG_TYPE(int code, String name) {
this.code = code;
this.name = name;
}
}
消息处理类
基础接口:
public interface MessageService {
/**
* 消息处理
* @param messageInfo
*/
void handleMessage(MessageInfo messageInfo);
}
实现类:简单就例举两种消息类型
图片:
@MsgTypeHandler(MSG_TYPE.IMAGE)
@Service
public class ImgMsgServiceImpl implements MessageService {
@Override
public void handleMessage(MessageInfo messageInfo) {
System.out.println("处理图片消息 " + messageInfo.getContent());
}
}
文本:
@MsgTypeHandler(MSG_TYPE.TEXT)
@Service
public class TextMsgServiceImpl implements MessageService {
@Override
public void handleMessage(MessageInfo messageInfo) {
System.out.println("处理文本消息 " + messageInfo.getContent());
}
}
可以看到服务类上有个注解 @MsgTypeHandler(MSG_TYPE.TEXT),这是一个自定义注解表示该类具体处理哪种类型的消息
自定义注解
/**
* 注解意义分别为 该注解可用于标注类型 编译参与阶段选择 继承
*/
@Documented
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MsgTypeHandler {
/*
*此处即是注解括号中包含的值的类型 例:@MsgTypeHandler(MSG_TYPE.TEXT)
*/
MSG_TYPE value();
}
为什么要定义这个注解呢,当程序启动开始初始化时,将所有对应的处理类存入容器中,然后通过消息的类型取出容器中对应的service类,直接处理即可完成功能。提到容器和对应, 那肯定是需要一个容器和相应的Map才能完成想要的效果了。上代码
容器
@Component
public class MessageServiceContext {
private final Map<Integer, MessageService> handlerMap = new HashMap<>();
public MessageService getMessageService(Integer type) {
return handlerMap.get(type);
}
public void putMessageService(Integer code, MessageService messageService) {
handlerMap.put(code, messageService);
}
}
初始化策略
@Component
public class MessageServiceListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 初始化时通过注解获取所有的消息处理服务对象
Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(MsgTypeHandler.class);
// 将服务类对象存入容器
MessageServiceContext messageServiceContext = event.getApplicationContext().getBean(MessageServiceContext.class);
//map的foreach是对k,v键值对进行遍历,而且取出的还是Object对象所以需要强转
beans.forEach((name,bean)->{
MsgTypeHandler typeHandler = bean.getClass().getAnnotation(MsgTypeHandler.class);
messageServiceContext.putMessageService(typeHandler.value().code,(MessageService) bean);
});
}
}
这样就完成了,当程序启动,所有注解了对应消息类型的Msghandler的service一开始就会被装入容器中,然后具体调用业务时就是这样的。
测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class MsgServiceTest {
@Autowired
private MessageServiceContext messageServiceContext;
@Test
public void contextLoads() {
// 构建一个文本消息
MessageInfo messageInfo = new MessageInfo(MSG_TYPE.TEXT.code, "消息内容");
// 从容器中获取到对应的服务类并处理
MessageService messageService = messageServiceContext.getMessageService(messageInfo.getMessageCode());
messageService.handleMessage(messageInfo);
}
}
总结
个人理解吧,的确是可以很好地简化这种多类型匹配判断的处理,扩展或者修改也只需要修改枚举类添加新的类型,以及相应的处理类。省下了大量的if/else或者switch匹配的空间,代码看起来简洁了许多。
总之这只是对热门的大佬的帖子的我自己的知识吸收和理解以及整理,起因是看到下面有没怎么看懂的老哥和不想自己也渐渐忘记了,觉得很有用处来着,希望不要别喷抄袭什么的。
谢谢大佬(doge):CSDN博主「Java识堂」的原创文章,原文链接:https://blog.csdn.net/zzti_erlie/article/details/102988486