介紹
最近跟著公司的大佬開發了一款IM系統,類似QQ和微信哈,就是聊天軟件。我們有一部分業務邏輯是這樣的
if (msgType = "文本") {
// dosomething
} else if(msgType = "圖片") {
// doshomething
} else if(msgType = "視頻") {
// doshomething
} else {
// doshomething
}
就是根據消息的不同類型有不同的處理策略,每種消息的處理策略代碼都很長,如果都放在這種if else代碼快中,代碼很難維護也很醜,所以我們一開始就用了策略模式來處理這種情況。
策略模式還挺簡單的,就是定義一個接口,然後有多個實現類,每種實現類封裝了一種行為。然後根據條件的不同選擇不同的實現類。
實現過程
消息對象,當然真實的對象沒有這麼簡單,省略了很多屬性
@Data
@AllArgsConstructor
public class MessageInfo {
// 消息類型
private Integer type;
// 消息內容
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());
}
}
文章寫到這,可能大多數人可能會想到要需要如下一個Map, Map,這樣直接根據消息類型就能拿到消息處理對象,調用消息處理對象的方法即可。我們就是這樣做的,但是我們不想手動維護這個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> handlerMap = new HashMap<>();/<integer>
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> {/<contextrefreshedevent>
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<string> beans = event.getApplicationContext().getBeansWithAnnotation(MsgTypeHandler.class);/<string>
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, "消息內容");
MessageService messageService = messageServiceContext.getMessageService(messageInfo.getType());
// 處理文本消息 消息內容
// 可以看到文本消息被文本處理類所處理
messageService.handleMessage(messageInfo);
}
測試類正常工作,通過策略模式避免了寫大量的if else代碼,也更容易維護
閱讀更多 風塵散人 的文章