【JAVA設計模式-第四課】觀察者模式-屌絲求職記+新聞訂閱html
觀察者模式又稱依賴模式或發佈-訂閱模式。java
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生變化時,全部依賴它的對象都獲得通知並被自動更新。程序員
原文:編程
Observer Pattern(Another Name:Dependents , Publish-Subscribe)設計模式
Define a one-to-many dependency between objects so that when one object changes state , all its dependents are notified and update automaically .服務器
--《Design Patterns》GOF 架構
在實際編程中,咱們常常會遇到多個對象請求一個對象,或者說多個對象須要同一個對象的數據變化,並且這多個對象都但願跟蹤這個特殊對象中的數據變化。舉個簡單的例子:新浪微博中的關注。小弟比較喜歡一些冷笑話、內涵圖什麼的,因此就關注了@冷笑話、@內涵圖什麼的。固然除了我以外,還有其餘屌絲男屌絲女喜歡冷笑話。因此當冷笑話發表一條微博的時候,全部關注它的用戶都將收到這條微博信息。這是一對多的關係,並且有點數據「推送」(觀察者模式中確實存在數據的「推/拉」,下文也會講解關於觀察者模式中的數據「推/拉」)的味道。好了,有那麼一天,我不喜歡這個冷笑話,那麼我就取消了對它的關注,之後呢,也就天然而然的看不到它的微博動態變化了。ide
好、言歸正傳。post
觀察者模式是關於多個對象想知道一個對象中數據變化狀況的一種成熟模式。在觀察者模式中有一個稱做「主題」的對象和若干個稱做「觀察者」的對象。主題和觀察者的之間的關係是一對多的關係。就像微博用戶@冷笑話和關注它的衆屌絲們(我也是其中一員),@冷笑話就是「主題」,而咱們就是「觀察者」。固然了,你立刻就會想到,觀察者不一樣樣能夠擁有多個主題嗎?固然能夠了,若是這樣的話,也就成了多對多的關係了。固然咱們若是須要的話,也可將多對多關係細化爲雙向的一對多關係(即一個主題可有多個觀察者,一個觀察者一樣能夠擁有多個主題。)。STOP!!扯遠了。根據《設計模式》原文咱們能夠知道「觀察者模式」是針對多個對象與一個對象之間數據的關係,並且強調的是這多個對象都想知道這惟一對象的數據變化。當「主題」的狀態發生了變化,那麼全部的「觀察者」都將獲得通知。大數據
下面簡單的闡述下「觀察者模式」中角色及角色所擔任的職責。
下面咱們於「屌絲求職」爲例子,屌絲求職者爲「具體觀察員」(ConcreteObServer),求職中心爲「具體主題」(ConcreteSubject)。求職者要想知道求職中心的數據信息,首先要在求職中心登記本身的信息,這樣求職中心才能向求職者發送(招聘)信息;當求職者不想收到求職中心的數據信息時,就能夠要求求職中心(工做人員)刪除本身的信息。
觀察者模式的UML類圖,以下:
代碼實現,以下:
1 /** 2 * 觀察者-定義一個更新數據的方法 3 * @author <a href="mailto:weijunqiang2010@gmail.com">Ajunboys</a> 4 * 5 * @since 2013-6-4 6 */ 7 public interface ObServer { 8 /** 9 * 經過查看郵箱,來獲取求職中心想觀察者發送的數據信息(至關於UML類圖中的update()方法) 10 * @param message 11 */ 12 public abstract void seeMail(String message); 13 }
1 /** 2 * 主題-定義了增長、刪除以及向觀察者更新數據的方法 3 * @author <a href="mailto:weijunqiang2010@gmail.com">Ajunboys</a> 4 * 5 * @since 2013-6-4 6 */ 7 public interface Subject { 8 /** 9 * 增長觀察者 10 * @param obServer 11 */ 12 public abstract void addObserver(ObServer obServer); 13 /** 14 * 刪除觀察者 15 * @param obServer 16 */ 17 public abstract void deleteObserver(ObServer obServer); 18 /** 19 * 通知觀察者信息更新 20 */ 21 public abstract void notifyObservers(); 22 }
1 import java.util.ArrayList; 2 import java.util.List; 3 4 /** 5 * 具體主題 6 * @author <a href="mailto:weijunqiang2010@gmail.com">Ajunboys</a> 7 * 8 * @since 2013-6-4 9 */ 10 public class ConcreteSubject implements Subject{ 11 private String message; 12 //信息是否存在或最新 13 boolean changed; 14 15 //存放觀察者引用的集合 16 private List<ObServer> obServers ; 17 18 public ConcreteSubject() { 19 obServers = new ArrayList<ObServer>(); 20 message = ""; 21 changed = false; 22 } 23 24 @Override 25 public void addObserver(ObServer obServer) { 26 if (!obServers.contains(obServer)) 27 obServers.add(obServer); 28 } 29 30 @Override 31 public void deleteObserver(ObServer obServer) { 32 if(obServers.contains(obServer)) 33 obServers.remove(obServer); 34 } 35 36 @Override 37 public void notifyObservers() { 38 if (changed) { 39 //通知全部觀察者 40 for (ObServer obServer : obServers) { 41 //查看郵件信息 42 obServer.seeMail(message); 43 } 44 changed = false; 45 } 46 } 47 48 /** 49 * 設置最新的招聘信息 50 * @param mess 51 */ 52 public void newMessage(String mess){ 53 if (mess.equals(message)) { 54 changed = false; 55 }else { 56 message = mess; 57 changed = true; 58 } 59 } 60 }
1 /** 2 * 具體觀察者-程序員 3 * @author <a href="mailto:weijunqiang2010@gmail.com">Ajunboys</a> 4 * 5 * @since 2013-6-6 6 */ 7 public class Programmer implements ObServer{ 8 private Subject subject; 9 10 /** 11 * 12 * @param subject 13 */ 14 public Programmer(Subject subject){ 15 this.subject = subject; 16 subject.addObserver(this); 17 } 18 19 @Override 20 public void seeMail(String message) { 21 System.out.println("【通知】程序員:"); 22 System.out.println(message); 23 } 24 25 }
1 /** 2 * 具體觀察者-工程師 3 * @author <a href="mailto:weijunqiang2010@gmail.com">Ajunboys</a> 4 * 5 * @since 2013-6-6 6 */ 7 public class Engineer implements ObServer{ 8 private Subject subject; 9 10 11 /** 12 * @param subject 13 */ 14 public Engineer(Subject subject) { 15 this.subject = subject; 16 subject.addObserver(this); 17 } 18 19 20 @Override 21 public void seeMail(String message) { 22 System.out.println("【通知】軟件工程師:"); 23 System.out.println(message); 24 } 25 26 }
演示一個程序員和工程師手收到求職中心發來的消息
1 /** 2 * 觀察者模式-應用 3 * @author <a href="mailto:weijunqiang2010@gmail.com">Ajunboys</a> 4 * 5 * @since 2013-6-6 6 */ 7 public class Main { 8 public static void main(String[] args) { 9 JobCenter center = new JobCenter(); 10 Programmer programmer = new Programmer(center); 11 Engineer engineer = new Engineer(center); 12 center.newMessage("野狼軟件開發工做室須要 5 個JAVA程序員、1個JAVA軟件工程師"); 13 center.notifyObservers(); 14 center.newMessage("科騰遊戲須要 2 個視覺設計師"); 15 center.notifyObservers(); 16 17 } 18 }
【通知】程序員: 野狼軟件開發工做室須要 5 個JAVA程序員、1個JAVA軟件工程師 【通知】軟件工程師: 野狼軟件開發工做室須要 5 個JAVA程序員、1個JAVA軟件工程師 【通知】程序員: 科騰遊戲須要 2 個視覺設計師 【通知】軟件工程師: 科騰遊戲須要 2 個視覺設計師
以前,舉了個關於微博關注的例子,就是@冷笑話什麼的。其實在微博中,對數據處理是有「推」數據和「拉」數據的。有篇博文叫「新浪微博網站的技術架構詳細分析 講解到不少技術難點分解與實現方法」 ,博文裏也有說起新浪微博在處理大數據時的技術實現,其中就有提到「推」數據這部分。
下圖:爲博文部分截圖。
推數據方式指具體主題將變化後的數據所有交給(通知)具體觀察者,即將變化後的數據傳遞給具體觀察者用於更新方法的參數。當具體主題認爲具體觀察者須要這些變換後的所有數據時每每採用推數據方式。好比:@冷笑話有不少粉絲,它發表了一條新的微博,那麼它就會通知全部關注它的粉絲(可能只是已登陸在線的粉絲。)本身的動態。再如上面代碼實現觀察者模式中就是一種主題向觀察者發送數據,也是「推」數據的方式。
拉數據方式是指具體主題不將變化後的數據交給(通知)具體觀察者,而是提供了得到這些數據的方法,具體觀察者在獲得通知後,能夠調用具體主題提供的方法獲得數據(觀察者本身把數據"拉"取出來),但須要本身判斷數據是否發生了變化。當具體主題不知道具體觀察者是否須要這些變換後的數據時每每採用拉數據的方式。好比:在新聞中心,客戶端會主動經過一些方法(HTTP定時器拉數據、輪詢等)向後臺服務器獲取是否有最新的(未讀取)新聞動態。
我的理解:服務器主動向用戶(客戶端)發送數據,就是「推」數據;而用戶(客戶端)向服務器主動請求數據,則是「拉」數據。
下面接着模擬一下觀察者模式中的「拉」數據。
下一課:【JAVA設計模式-第四課】觀察者模式-屌絲求職記+新聞訂閱
--------------------------------------------------------------
參考資料:《JAVA設計模式》