继夫的玩弄H辣文的小说|女人与拘性猛交视频|精品欧美高清不卡高清|一起做亏亏的事情的视频|啦啦啦在线视频观看|望月直播下载ios版本|国产日韩欧美一区二区三区

設計模式私人筆記-觀察者模式

前言:設計模式,一個老掉牙的話題java實現設計模式,一個永恒的話題 。所有的Java開發,都必須經過的話題,都必須學習的話題 。但是其實很多時候自己只是看了,了解了,然后忘了 。所以還是想從頭梳理一遍重要的設計模式,給自己看 。不求邏輯多清晰,內容多完善,UML圖畫的多嚴謹,理解、會意就好 。
遇到什么問題
提出一個經典的需求:假設現在有一個報價系統,當某個產品的價格發生變化時,就會通過短信發送給相關客戶 , 也會通過郵件給該客戶發送價格變動情況 。
用最經典的方法來實現該需求 。這里只是做了一個非常簡化的實現 。
創建兩個服務,一個用于發送短信,一個用于發送郵件:
創建一個服務,用于處理價格變動的邏輯:
基于該設計的使用代碼:
該設計方式非常簡單 , 也是非常非常常見的實現方案 。但是該方案很明顯存在一些問題:
· 兩個感知價格變動的服務和,沒有抽象出接口,雖然感知價格的方法一致;
· 如果要新添加感知價格變動的服務 , 比如我現在要求把每次變動的價格計入日志系統 , 又需要增加一個實體類 ;并且,每次增加新的價格變動感知服務,都要添加為的成員,然后由手動去依次調用;
簡單來說 , 就是,這種實現方案,擴展性太差!
應用觀察者模式
先簡單說明一下什么是觀察者模式 。舉個經典的案例:公眾號的 。當一個用戶了某個公眾號,就相當于該公眾號把用戶添加到了用戶列表,這樣一來,只要該公眾號有消息更新,即可按照用戶列表中的用戶依次推送消息 。如果用戶不愿意收到該公眾號的信息,就取消即可 。
在Head First Java 一書中,把觀察者模式定義為:
觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,他所有依賴者都會收到通知并執行更新 。
· 有狀態對象的狀態更新;
· 通知所有注冊的觀察者進行更新; 其實就這兩步 。下面先用觀察者模式重寫上面的案例 。
首先為主題對象(有狀態對象)定義一個接口:
【設計模式私人筆記-觀察者模式】· 注意,第一個方法用于注冊價格感知對象,即之前的,,或者其他的需要監控價格變化的對象;
第二步,為所有需要感知價格變化的業務統一抽取一個接口:
該接口中就一個方法 , 執行價格的改變感應;那么方法傳入的price就是變化之后的price;
然后重新實現發送郵件和短信的服務:
在之前的版本之上 , 只是實現了接口;
最后,重新修改我們的價格改變對象:
在之前的版本基礎上,實現了接口,因為一對多,先簡單的使用List來存放 。在方法中,分別調用注冊的每一個價格感知器的業務方法 。
這個版本的測試代碼:
好處
· 完全面向接口編程;
· 擴展方便,如果我現在要增加記錄日志的功能,只需要:
不需要修改本身的代碼,只需要在執行之前java實現設計模式,把注冊到中:
即可 。
標準的觀察者模式
上面的例子,僅僅只是應用觀察者模式的案例,現在來抽象出標準的觀察者模式 , 看類圖:
有幾個點注意:
· :抽象主題(接口) , 就是包含狀態發生改變的那個類的抽象;在該接口中 , 定義了添加觀察者,刪除觀察者和通知觀察者三個方法;
· :主題接口的實現;
· :抽象的觀察者(接口),就是上面說到的感知改變的對象的抽象 , 定義了一個感知改變之后的業務邏輯方法 。這個方法中定義了兩個參數,(本次產生改變的主題對象),(本次變化的數據),關于這兩個參數 , 涉及到改變數據的推送和拉取兩種模式,后面介紹,這里就簡單理解為用這兩個參數來獲取改變的數據就可以了(第二個看成上面的即可) 。
下面寫一段標準觀察者模式示例代碼 。
定義接口:
定義觀察者接口:

設計模式私人筆記-觀察者模式

文章插圖
設計模式私人筆記-觀察者模式

文章插圖
定義具體的主題類實現,仍然先使用簡單的List來存儲注冊的觀察者;
最后做一個觀察者的實現:
使用起來也很簡單,只需要和上面示例中一樣 , 創建好具體的主題對象和具體的觀察者,將觀察者注冊到主題對象中即可 。
Java中的觀察者模式
因為觀察者模式使用非常頻繁,甚至被譽為設計模式的皇后(不做評價),所以Java其實已經提供了觀察者模式的實現 。
在java.util包中 , 有兩個類型:
· java.util.:可被觀察對象,即我們的;
· java.util.:觀察對象,就是我們上面的;
我們來看看這兩個類的結構,有一個直觀了解:
首先 , 接口的實現和我們上面標準的觀察者接口定義相同,仍然支持推送和拉取兩種獲取變化數據的方式,不過多說明;
下面重點看看定義:
注意到幾個點: 1,首先,是一個抽象類 。我們知道,不建議將這種抽象定義為抽象類,會讓我們的類組織關系變得非常死板,這是設計上的一個問題,我們先跳過(這個類是JDK1.0就存在了,兼容問題沒有更新,后面會介紹更好的方法) 。2,我們看到了和方法 , 很明顯,這兩個方法用于維護觀察者; 3,方法,顧名思義 , 用于移除掉所有該主題上的觀察者; 4,方法和()兩個方法,很明顯都是告知觀察者的方法,只是一個可以主動傳遞改變的值(推送),一個不需要傳遞改變的值(這個值可以主題對象通過方法提供,即拉?。?。4, , 和,這三個方法比較奇怪,肯定是屬于一組內容的,具體是什么 , 我們后面介紹 。這里只需要注意一個重點,就是在我們調用方法之前,我們必須調用方法,告知我們的改變已經成立(為什么這么設計,后面討論)
如果還有點模糊,我們直接使用java提供的觀察者模式支持類,完成我們的價格監控的實例:
從最簡單的發送郵件服務開始:
我們只需要實現接口,然后在方法中完成邏輯即可 。這里需要注意,我們采用的是推送的方式,即我們直接從第二個參數中獲取了price的數值,那么意味著主題對象中,需要調用()方法來推送數據(注意觀察后面的代碼);
記錄日志服務:
接下來,主題對象的實現:
注意代碼中的注釋,我們在方法調用之前,一定要調用方法 。
基于Java的觀察者模式的測試代碼:
測試代碼很簡單 。
小結,那么Java提供的觀察者模式到底為我們做了些什么?很明顯 , 主題對觀察者的維護工作,主題對觀察者的通知動作,都放到了類中 。那我們來簡單分析一下類的源碼 , 主要解決兩個問題:
· 怎么維護觀察者;
· 方法到底用來干嘛;
類源碼分析
· 維護兩個成員變量,和obs,其中代表數據是否確定改變,obs是一個的 , 雖然性能差,但確是線程安全的,這點是選用的原因 。
· 構造方法中初始化;
· 這里列出了和觀察者相關的所有方法,首先注意,方法都是的,保證了線程安全;
· 這是關于通知觀察者,調用觀察者更新方法的代碼 。在代碼注釋中已經說明兩個方法的區別;
· 在方法中,我們注意到聲明了一個局部變量[] ;并使用 = obs.();將當前中的觀察者拷貝到了中,最后在執行通知的時候 , 遍歷的是中的觀察者引用;這樣做的目的在于,使用作為一個當前執行通知時的觀察者快照,那么,在真正只是方法的時候 , 不會造成正常的添加和刪除;對并發下的性能有一定幫助;但是可能會存在兩個問題需要注意:
· 可能會有剛加入的觀察者沒法及時獲取正在變更的數據;
· 可能會有剛刪除的觀察者仍然受到了最近正在變更的數據;
· 代碼中,使用if(!) 來判定數據是否真正改變 。接下來看看相關的方法:
使用了三個方法來操作變量,代碼很簡單 。但是我們通過這兩段代碼,也能清楚的看出 , 為什么我們在調用方法之前,一定要調用方法了 。
可能有童鞋會覺得這樣的設計有點多此一舉,但是考慮到,如果一個數據變化過于頻繁,那么我們在某些情況下,也可以通過變量來控制真正的發送數據變更消息的時機,這也是一個設計初衷吧 。
Java的體系有什么問題?
上面已經看了Java本身提供的觀察者模式的具體使用和相關代碼實現,那么,這樣設計有問題么?
回答是肯定的 。有問題 。所以其實我們發現 , 真正使用Java的體系的代碼也不多 。主要原因我認為有兩個: 1,是一個類 , 這個本身就是很煩的事情,我的主題類需要繼承它,代碼結構會有問題; 2 , 中重要的方法,比如方法,導致除了繼承 , 沒法在外部通過第三方代碼來促使主題確定改變,在一些場景中,有較大局限性 。
一些相關話題
上面已經對觀察者模式做了較完整的梳理,完了么?還沒有 。從觀察者,我們至少能夠延伸出下面兩個問題:
· 觀察者模式和事件監聽模式有什么關系?
· 觀察者模式和發布訂閱模式有什么關系?
這里又引出了兩個模式:事件監聽模式和發布訂閱模式,這兩個模式又可以引申出一大堆問題 。我們在分文章來獨立闡述 。
本文到此結束,希望對大家有所幫助 。