專案中的if else太多了,該怎麼重構?

Java識堂發表於2020-01-11

在這裡插入圖片描述

介紹

最近跟著公司的大佬開發了一款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, 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, "訊息內容");
	MessageService messageService = messageServiceContext.getMessageService(messageInfo.getType());
	// 處理文字訊息 訊息內容
	// 可以看到文字訊息被文字處理類所處理
	messageService.handleMessage(messageInfo);
}

測試類正常工作,通過策略模式避免了寫大量的if else程式碼,也更容易維護

歡迎關注

在這裡插入圖片描述

參考部落格

[1]https://juejin.im/post/5c5172d15188256a2334a15d

相關文章