哦哇資訊網

淺談策略模式在訊息轉發場景下的應用

由 插猹的閏土 發表于 美食2023-01-23

背景

在上一篇文章中,我們介紹瞭如何設計一個訊息中心,傳送門 《如何設計一個訊息中心》

有了承載這些訊息的地方後,接下來的問題便是,

這些訊息從哪裡來?

通常對於一個內容型產品來說,在其互動體系中,為了增強訊息的使用者觸達,增強使用者的互動心智,在互動(評論、點贊等)行為發生後,會將互動訊息推送至訊息中心,然後根據不同的互動行為型別匹配不同的訊息模版。

然而隨著互動行為種類的增加(內容的點贊、評論的點贊……),不斷的透過 if…else 來根據不同的訊息型別生成不同的訊息模版會使得業務程式碼愈發複雜,難以維護。

不僅如此,一旦需要增加一種新的互動訊息時,需要對原有程式碼進行破壞性修改,違背了“開閉原則”。因此有必要對互動行為訊息轉發至訊息中心這一場景進行抽象,讓後續的維護者、建設者只需要關心某一特定的互動行為訊息即可(我可不想未來被別人噴在 山上拉 )。

策略模式

在說明具體的實現方案前,我們先介紹一個設計模式——策略模式。

策略模式,英文全稱是 Strategy Design Pattern。在 GoF 的《設計模式》一書中,它是這樣定義的:

Define a family of algorithms, encapsulate each one, and make them interchangeable。 Strategy lets the algorithm vary independently from clients that use it。

翻譯成中文就是:定義一簇演算法類,將每個演算法分別封裝起來,讓它們可以互相替換。策略模式可以使演算法的變化獨立於使用它們的客戶端(這裡的客戶端代指使用演算法的程式碼)。

策略模式用來解耦策略的

定義、建立、使用

。實際上,一個完整的策略模式就是由這三個部分組成的。

策略類的定義比較簡單,包含一個策略介面和一組實現這個介面的策略類。

策略的建立由工廠類來完成,封裝策略建立的細節。

策略模式包含一組策略可選,客戶端程式碼如何選擇使用哪個策略,有兩種確定方法:編譯時靜態確定和執行時動態確定。其中,“執行時動態確定”才是策略模式最典型的應用場景。

實現方案

在對策略模式有了基本的瞭解後,我們嘗試在本節將其運用起來。

仔細分析了第一章的應用場景後我們發現其實實現鏈路並不複雜,整體流程如下圖所示:

在本例中,根據不同的互動行為型別,我們將點贊訊息和評論訊息分成以下幾類:

點贊類:

內容點贊

評論點贊

評論類:

內容評論

內容評論的回覆

轉發策略的定義

整個方案中最重要的一環是對轉發策略的匹配,因此第一步我們要做的應該是定義一個策略。為了方便後續的擴充套件(未來可能會有多種轉發策略),我們此處定義一個策略介面

MsgTransmitStrategy

public interface MsgTransmitStrategy { // 是否命中策略 boolean match(T message); // 訊息型別 TransmitMsgType getMsgType(); // 建立需要轉發的訊息實體 MessageContent createMessageContent(T message);}

每個策略需要具備的行為能力應該有:

明確自己是否命中了轉發策略:

match(T message)

明白自己要轉發的是什麼型別的訊息:

getMsgType()

建立要轉發的訊息:

createMessageContent(T message)

轉發策略的建立

以點贊訊息為例,上文提到存在兩種點贊訊息的轉發策略:內容點贊與評論點贊。

因此我們需要建立兩個具體的策略:

ContentLikeMsgTransmitStrategy

CommentLikeMsgTransmitStrategy

@Componentpublic class ContentLikeMsgTransmitStrategy implements MsgTransmitStrategy { @Override public TransmitMsgType getMsgType() { return TransmitMsgType。CONTENT_LIKE; } @Override public boolean match(String message) { // do something return true; } @Override public MessageContent createMessageContent(String message) { // do something…………… return messageContent; }}

@Componentpublic class CommentLikeMsgTransmitStrategy implements MsgTransmitStrategy { @Override public TransmitMsgType getMsgType() { return TransmitMsgType。COMMENT_LIKE; } @Override public boolean match(String message) { // do something return true; } @Override public MessageContent createMessageContent(String message) { // do something…………… return messageContent; }}

轉發策略的使用

現在,我們已經建立了幾種訊息轉發策略了,那麼客戶端程式碼一般如何確定使用哪個策略呢?最常見的是執行時動態確定使用哪種策略,這也是策略模式最典型的應用場景。

這裡的“執行時動態”指的是,我們事先並不知道會使用哪個策略,而是在程式執行期間,根據配置、使用者輸入、計算結果等這些不確定因素,動態決定使用哪種策略。

我們來建立一個

MsgTransmitExecutor

作為策略的執行器,透過遍歷的手段依次呼叫每個策略的 match 方法,符合條件的策略類可以執行統一的轉發方法。

@Componentpublic class MsgTransmitExecutor { @Resource private MsgTransmitTools msgTransmitTools; @Resource private BentleyManager bentleyManager; public void execute(T message, List> strategyList) { for (MsgTransmitStrategy strategy : strategyList) { // 依次呼叫策略的match方法 if (strategy。match(message)) { MessageContent messageContent = strategy。createMessageContent(message); Optional。ofNullable(messageContent) 。filter(mc -> msgTransmitTools。isNeedSendPush(mc)) 。ifPresent(mc -> bentleyManager。sendMessage(mc)); } } }}

那麼,對於客戶端(實際呼叫策略的類),只需透過組合(而非繼承)的方式,將策略的執行器注入到具體的訊息處理邏輯中即可。

public class LikeMsgListener extends MessageConsumer { /** * 點贊訊息轉發策略集合 */ private final List> LIKE_MSG_TRANSMIT_STRATEGY_LIST = Lists。newArrayList(); @Resource private MsgTransmitExecutor msgTransmitExecutor; @Resource private ContentLikeMsgTransmitStrategy contentLikeMsgTransmitStrategy; @Resource private CommentLikeMsgTransmitStrategy commentLikeMsgTransmitStrategy; @PostConstruct public void init() { LIKE_MSG_TRANSMIT_STRATEGY_LIST。addAll(Arrays。asList( contentLikeMsgTransmitStrategy, commentLikeMsgTransmitStrategy )); } @Override protected ConsumeConcurrentlyStatus processMessage(String message, MessageExt messageExt) { try { msgTransmitExecutor。execute(message, LIKE_MSG_TRANSMIT_STRATEGY_LIST); } catch (Exception e) { PgLog。messageBoxError(“LikeListener”, e); } return ConsumeConcurrentlyStatus。CONSUME_SUCCESS; }}

總結

在上述例項中,透過 LikeMsgListener 監聽點贊類訊息。透過 MsgTransmitStrategy 介面定義訊息轉發至訊息中心的行為策略,MsgTransmitExecutor 作為策略的執行器最終實現將匹配過後的訊息以不同的模版型別推送至訊息中心。

整個方案,透過利用策略模式避免了整個轉發場景中使用多重條件判斷,維護者只需專注於當前的轉發策略即可,遵循了“開閉原則”,同時透過組合而非繼承的方式注入策略執行器,擴充套件性較好。

但任何事物都具有兩面性,一旦未來的場景變得更加複雜(例如點贊行為可以支援更多型別),轉發策略類會增多,屆時就需要考慮使用一些混合模式(例如策略也可以使用工廠模式建立等方法),解決業務發展所帶來的策略類膨脹的問題了。

TAG: 策略轉發message訊息點贊