java 观察者模式_重学 Java 设计模式:实战观察者模式「模拟类似小客车指标摇号过程,监听消息通知用户中签场景」...
一、前言
知道的越多不知道的就越多
編程開發(fā)這條路上的知識(shí)是無窮無盡的,就像以前你敢說精通Java,到后來學(xué)到越來越多只想寫了解Java,過了幾年現(xiàn)在可能想說懂一點(diǎn)點(diǎn)Java。當(dāng)視野和格局的擴(kuò)大,會(huì)讓我們?cè)絹碓桨l(fā)現(xiàn)原來的看法是多么淺顯,這就像站在地球看地球和站在宇宙看地球一樣。但正因?yàn)樾貞押脱劢绲奶嵘屛覀冇辛烁嗟恼J(rèn)識(shí),也逐漸學(xué)會(huì)了更多的技能。雖然不知道的越來越多,但也因此給自己填充了更多的技術(shù)棧,讓自己越來越強(qiáng)大。
拒絕學(xué)習(xí)的惰性很可怕
現(xiàn)在與以前不一樣,資料多、途徑廣,在這中間夾雜的廣告也非常多。這就讓很多初學(xué)者很難找到自己要的知識(shí),最后看到有人推薦相關(guān)學(xué)習(xí)資料立刻屏蔽、刪除,但同時(shí)技術(shù)優(yōu)秀的資料也不能讓需要的人看見了。久而久之把更多的時(shí)間精力都放在游戲、娛樂、影音上,適當(dāng)?shù)姆潘墒强梢缘?#xff0c;但往往沉迷以后就很難出來,因此需要做好一些可以讓自己成長(zhǎng)的計(jì)劃,稍有克制。
平衡好軟件設(shè)計(jì)和實(shí)現(xiàn)成本的度°
有時(shí)候一個(gè)軟件的架構(gòu)設(shè)計(jì)需要符合當(dāng)前條件下的各項(xiàng)因素,往往不能因?yàn)樾闹邢氘?dāng)然的有某個(gè)藍(lán)圖,就去開始執(zhí)行。也許雖然你的設(shè)計(jì)是非常優(yōu)秀的,但是放在當(dāng)前環(huán)境下很難滿足業(yè)務(wù)的時(shí)間要求,當(dāng)一個(gè)業(yè)務(wù)的基本訴求不能滿足后,就很難拉動(dòng)市場(chǎng)。沒有產(chǎn)品的DAU支撐,最后整個(gè)研發(fā)的項(xiàng)目也會(huì)因此停滯。但研發(fā)又不能一團(tuán)亂麻的寫代碼,因此需要找好一個(gè)適合的度,比如可以搭建良好的地基,實(shí)現(xiàn)上可擴(kuò)展。但在具體的功能上可以先簡(jiǎn)化實(shí)現(xiàn),隨著活下來了再繼續(xù)完善迭代。
二、開發(fā)環(huán)境
JDK 1.8
Idea + Maven
三、觀察者模式介紹
簡(jiǎn)單來講觀察者 模式,就是當(dāng)一個(gè)行為發(fā)生時(shí)傳遞信息給另外一個(gè)用戶接收做出相應(yīng)的處理,兩者之間沒有直接的耦合關(guān)聯(lián)。例如;狙擊手、李云龍。
除了生活中的場(chǎng)景外,在我們編程開發(fā)中也會(huì)常用到一些觀察者的模式或者組件,例如我們經(jīng)常使用的MQ服務(wù),雖然MQ服務(wù)是有一個(gè)通知中心并不是每一個(gè)類服務(wù)進(jìn)行通知,但整體上也可以算作是觀察者模式的思路設(shè)計(jì)。再比如可能有做過的一些類似事件監(jiān)聽總線,讓主線服務(wù)與其他輔線業(yè)務(wù)服務(wù)分離,為了使系統(tǒng)降低耦合和增強(qiáng)擴(kuò)展性,也會(huì)使用觀察者模式進(jìn)行處理。
四、案例場(chǎng)景模擬
http://weixin.qq.com/r/W0Rqco7EptPZrcoR9xFJ (二維碼自動(dòng)識(shí)別)
在本案例中我們模擬每次小客車指標(biāo)搖號(hào)事件通知場(chǎng)景(真實(shí)的不會(huì)由官網(wǎng)給你發(fā)消息)
可能大部分人看到這個(gè)案例一定會(huì)想到自己每次搖號(hào)都不中的場(chǎng)景,收到一個(gè)遺憾的短信通知。當(dāng)然目前的搖號(hào)系統(tǒng)并不會(huì)給你發(fā)短信,而是由百度或者一些其他插件發(fā)的短信。那么假如這個(gè)類似的搖號(hào)功能如果由你來開發(fā),并且需要對(duì)外部的用戶做一些事件通知以及需要在主流程外再添加一些額外的輔助流程時(shí)該如何處理呢?
基本很多人對(duì)于這樣的通知事件類的實(shí)現(xiàn)往往比較粗獷,直接在類里面就添加了。1是考慮 這可能不會(huì)怎么擴(kuò)展,2是壓根就沒考慮 過。但如果你有仔細(xì)思考過你的核心類功能會(huì)發(fā)現(xiàn),這里面有一些核心主鏈路,還有一部分是輔助功能。比如完成了某個(gè)行為后需要觸發(fā)MQ給外部,以及做一些消息PUSH給用戶等,這些都不算做是核心流程鏈路,是可以通過事件通知的方式進(jìn)行處理。
那么接下來我們就使用這樣的設(shè)計(jì)模式來優(yōu)化重構(gòu)此場(chǎng)景下的代碼。
1. 場(chǎng)景模擬工程
itstack這里提供的是一個(gè)模擬小客車搖號(hào)的服務(wù)接口。
2. 場(chǎng)景簡(jiǎn)述
2.1 搖號(hào)服務(wù)接口
public非常簡(jiǎn)單的一個(gè)模擬搖號(hào)接口,與真實(shí)公平的搖號(hào)是有差別的。
五、用一坨坨代碼實(shí)現(xiàn)
這里我們先使用最粗暴的方式來實(shí)現(xiàn)功能
按照需求需要在原有的搖號(hào)接口中添加MQ消息發(fā)送以及短消息通知功能,如果是最直接的方式那么可以直接在方法中補(bǔ)充功能即可。
1. 工程結(jié)構(gòu)
itstack這段代碼接口中包括了三部分內(nèi)容;返回對(duì)象(LotteryResult)、定義接口(LotteryService)、具體實(shí)現(xiàn)(LotteryServiceImpl)。
2. 代碼實(shí)現(xiàn)
public從以上的方法實(shí)現(xiàn)中可以看到,整體過程包括三部分;搖號(hào)、發(fā)短信、發(fā)MQ消息,而這部分都是順序調(diào)用的。
除了搖號(hào)接口調(diào)用外,后面的兩部分都是非核心主鏈路功能,而且會(huì)隨著后續(xù)的業(yè)務(wù)需求發(fā)展而不斷的調(diào)整和擴(kuò)充,在這樣的開發(fā)方式下就非常不利于維護(hù)。
3. 測(cè)試驗(yàn)證
3.1 編寫測(cè)試類
@Test測(cè)試過程中提供對(duì)搖號(hào)服務(wù)接口的調(diào)用。
3.2 測(cè)試結(jié)果
22:從測(cè)試結(jié)果上是符合預(yù)期的,也是平常開發(fā)代碼的方式,還是非常簡(jiǎn)單的。
六、觀察者模式重構(gòu)代碼
接下來使用觀察者模式來進(jìn)行代碼優(yōu)化,也算是一次很小的重構(gòu)。
1. 工程結(jié)構(gòu)
itstack觀察者模式模型結(jié)構(gòu)
從上圖可以分為三大塊看;事件監(jiān)聽、事件處理、具體的業(yè)務(wù)流程,另外在業(yè)務(wù)流程中 LotteryService 定義的是抽象類,因?yàn)檫@樣可以通過抽象類將事件功能屏蔽,外部業(yè)務(wù)流程開發(fā)者不需要知道具體的通知操作。
右下角圓圈圖表示的是核心流程與非核心流程的結(jié)構(gòu),一般在開發(fā)中會(huì)把主線流程開發(fā)完成后,再使用通知的方式處理輔助流程。他們可以是異步的,在MQ以及定時(shí)任務(wù)的處理下,保證最終一致性。
2. 代碼實(shí)現(xiàn)
2.1 事件監(jiān)聽接口定義
public接口中定義了基本的事件類,這里如果方法的入?yún)⑿畔㈩愋褪亲兓目梢允褂梅盒?lt;T>
2.2 兩個(gè)監(jiān)聽事件的實(shí)現(xiàn)
短消息事件
publicMQ發(fā)送事件
public以上是兩個(gè)事件的具體實(shí)現(xiàn),相對(duì)來說都比較簡(jiǎn)單。如果是實(shí)際的業(yè)務(wù)開發(fā)那么會(huì)需要調(diào)用外部接口以及控制異常的處理。
同時(shí)我們上面提到事件接口添加泛型,如果有需要那么在事件的實(shí)現(xiàn)中就可以按照不同的類型進(jìn)行包裝事件內(nèi)容。
2.3 事件處理類
public整個(gè)處理的實(shí)現(xiàn)上提供了三個(gè)主要方法;訂閱(subscribe)、取消訂閱(unsubscribe)、通知(notify)。這三個(gè)方法分別用于對(duì)監(jiān)聽時(shí)間的添加和使用。
另外因?yàn)槭录胁煌念愋?#xff0c;這里使用了枚舉的方式進(jìn)行處理,也方便讓外部在規(guī)定下使用事件,而不至于亂傳信息(EventType.MQ、EventType.Message)。
2.4 業(yè)務(wù)抽象類接口
public這種使用抽象類的方式定義實(shí)現(xiàn)方法,可以在方法中擴(kuò)展需要的額外調(diào)用。并提供抽象類abstract LotteryResult doDraw(String uId),讓類的繼承者實(shí)現(xiàn)。
同時(shí)方法的定義使用的是protected,也就是保證將來外部的調(diào)用方不會(huì)調(diào)用到此方法,只有調(diào)用到draw(String uId),才能讓我們完成事件通知。
此種方式的實(shí)現(xiàn)就是在抽象類中寫好一個(gè)基本的方法,在方法中完成新增邏輯的同時(shí),再增加抽象類的使用。而這個(gè)抽象類的定義會(huì)有繼承者實(shí)現(xiàn)。
另外在構(gòu)造函數(shù)中提供了對(duì)事件的定義;eventManager.subscribe(EventManager.EventType.MQ, new MQEventListener())。
在使用的時(shí)候也是使用枚舉的方式進(jìn)行通知使用,傳了什么類型EventManager.EventType.MQ,就會(huì)執(zhí)行什么事件通知,按需添加。
2.5 業(yè)務(wù)接口實(shí)現(xiàn)類
public現(xiàn)在再看業(yè)務(wù)流程的實(shí)現(xiàn)中可以看到已經(jīng)非常簡(jiǎn)單了,沒有額外的輔助流程,只有核心流程的處理。
3. 測(cè)試驗(yàn)證
3.1 編寫測(cè)試類
@Test從調(diào)用上來看幾乎沒有區(qū)別,但是這樣的實(shí)現(xiàn)方式就可以非常方便的維護(hù)代碼以及擴(kuò)展新的需求。
3.2 測(cè)試結(jié)果
23:從測(cè)試結(jié)果上看滿足 我們的預(yù)期,雖然結(jié)果是一樣的,但只有我們知道了設(shè)計(jì)模式的魅力所在。
七、總結(jié)
從我們最基本的過程式開發(fā)以及后來使用觀察者模式面向?qū)ο箝_發(fā),可以看到設(shè)計(jì)模式改造后,拆分出了核心流程與輔助流程的代碼。一般代碼中的核心流程不會(huì)經(jīng)常變化。但輔助流程會(huì)隨著業(yè)務(wù)的各種變化而變化,包括;營(yíng)銷、裂變、促活等等,因此使用設(shè)計(jì)模式架設(shè)代碼就顯得非常有必要。
此種設(shè)計(jì)模式從結(jié)構(gòu)上是滿足開閉原則的,當(dāng)你需要新增其他的監(jiān)聽事件或者修改監(jiān)聽邏輯,是不需要改動(dòng)事件處理類的。但是可能你不能控制調(diào)用順序以及需要做一些事件結(jié)果的返回繼續(xù)操作,所以使用的過程時(shí)需要考慮場(chǎng)景的合理性。
任何一種設(shè)計(jì)模式有時(shí)候都不是單獨(dú)使用的,需要結(jié)合其他模式共同建設(shè)。另外設(shè)計(jì)模式的使用是為了讓代碼更加易于擴(kuò)展和維護(hù),不能因?yàn)樘砑釉O(shè)計(jì)模式而把結(jié)構(gòu)處理更加復(fù)雜以及難以維護(hù)。這樣的合理使用的經(jīng)驗(yàn)需要大量的實(shí)際操作練習(xí)而來。
作者:小傅哥
鏈接:人類身份驗(yàn)證 - SegmentFault
總結(jié)
以上是生活随笔為你收集整理的java 观察者模式_重学 Java 设计模式:实战观察者模式「模拟类似小客车指标摇号过程,监听消息通知用户中签场景」...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 链表的基本操作——反转与删除
- 下一篇: Java常用设计模式————享元模式