李睿智
學號19021211293
轉載自https://blog.csdn.net/zzti_erlie/article/details/102988486
【嵌牛導讀】某公司的大佬開發(fā)了一款IM系統(tǒng),類似QQ和微信,也就是聊天軟件。就是根據(jù)消息的不同類型有不同的處理策略,每種消息的處理策略代碼都很長,如果都放在這種if else代碼快中,代碼很難維護也很丑,所以我們一開始就用了策略模式來處理這種情況。策略模式還挺簡單的,就是定義一個接口,然后有多個實現(xiàn)類,每種實現(xiàn)類封裝了一種行為。然后根據(jù)條件的不同選擇不同的實現(xiàn)類。
【嵌牛鼻子】聊天軟件、處理策略、接口、類封裝
【嵌牛提問】如果項目中的if else太多了,該怎么重構呢?
【嵌牛正文】
實現(xiàn)過程
消息對象,當然真實的對象沒有這么簡單,省略了很多屬性
@Data
@AllArgsConstructor
public class MessageInfo {
? ? // 消息類型
? ? private Integer type;
? ? // 消息內(nèi)容
? ? private String content;
}
定義一個消息處理接口
public interface MessageService {
? ? void handleMessage(MessageInfo messageInfo);
}
有2個消息處理接口,分別處理不同的消息
處理文本消息
@Service
@MsgTypeHandler(value = MSG_TYPE.TEXT)
public class TextMessageService implements MessageService {
? ? @Override
? ? public void handleMessage(MessageInfo messageInfo) {
? ? ? ? System.out.println("處理文本消息 " + messageInfo.getContent());
? ? }
}
處理圖片消息
@Service
@MsgTypeHandler(value = MSG_TYPE.IMAGE)
public class ImageMessageService implements MessageService {
? ? @Override
? ? public void handleMessage(MessageInfo messageInfo) {
? ? ? ? System.out.println("處理圖片消息 " + messageInfo.getContent());
? ? }
}
文章寫到這,可能大多數(shù)人可能會想到要需要如下一個Map, Map<消息類型,消息處理對象>,這樣直接根據(jù)消息類型就能拿到消息處理對象,調(diào)用消息處理對象的方法即可。我們就是這樣做的,但是我們不想手動維護這個Map對象,因為每次增加新的消息處理類,Map的初始化過程就得修改
我們使用了注解+ApplicationListener來保存這種映射關系,來看看怎么做的把
定義一個消息類型的枚舉類
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;
? ? }
}
定義一個注解
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MsgTypeHandler {
? ? MSG_TYPE value();
}
不知道你注意到了沒,前面的代碼中,每種消息處理類上面都有一個@MsgTypeHandler注解,表明了這個處理類
處理哪種類型的消息
@Service
@MsgTypeHandler(value = MSG_TYPE.TEXT)
public class TextMessageService implements MessageService {
? ? @Override
? ? public void handleMessage(MessageInfo messageInfo) {
? ? ? ? System.out.println("處理文本消息 " + messageInfo.getContent());
? ? }
}
用一個context對象保存了消息類型->消息處理對象的映射關系
@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);
? ? ? ? beans.forEach((name, bean) -> {
? ? ? ? ? ? MsgTypeHandler typeHandler = bean.getClass().getAnnotation(MsgTypeHandler.class);
? ? ? ? ? ? messageServiceContext.putMessageService(typeHandler.value().code, (MessageService) bean);
? ? ? ? });
? ? }
}
在spring的啟動過程中,通過解析注解,將消息類型->消息處理對象的映射關系保存到MessageServiceContext對象中
@Autowired
MessageServiceContext messageServiceContext;
@Test
public void contextLoads() {
// 構建一個文本消息
MessageInfo messageInfo = new MessageInfo(MSG_TYPE.TEXT.code, "消息內(nèi)容");
MessageService messageService = messageServiceContext.getMessageService(messageInfo.getType());
// 處理文本消息 消息內(nèi)容
// 可以看到文本消息被文本處理類所處理
messageService.handleMessage(messageInfo);
}
測試類正常工作,通過策略模式避免了寫大量的if else代碼,也更容易維護。