一、觀察者模式
觀察者模式,又可以稱之為發布-訂閱模式,觀察者,顧名思義,就是一個監聽者,類似監聽器的存在,一旦被觀察/監聽的目標發生的情況,就會被監聽者發現,這么想來目標發生情況到觀察者知道情況,其實是由目標將情況發送到觀察者的。
在現實生活中,警察抓小偷是一個典型的觀察者模式「這以一個慣犯在街道逛街然后被抓為例子」,這里小偷就是被觀察者,各個干警就是觀察者,干警時時觀察着小偷,當小偷正在偷東西「就給干警發送出一條信號,實際上小偷不可能告訴干警我有偷東西」,干警收到信號,出擊抓小偷。這就是一個觀察者模式
觀察者模式多用於實現訂閱功能的場景,例如微博的訂閱,當我們訂閱了某個人的微博賬號,當這個人發布了新的消息,就會通知我們。
1.1 特點:
(a) subject 和 observer之間是松耦合的,各自獨立實現,
(b) subject在發送廣播通知的時候,不需要指定具體的observer,observer可以自行決定是否要訂閱
(c) 遵循常用設計原則:高內聚,低耦合
1.2 觀察者模式的效果有以下的優點:
第一、觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體觀察者列表,每一個具體觀察者都符合一個抽象觀察者的接口。被觀察者並不認識任何一個具體觀察者,它只知道它們都有一個共同的接口。
由於被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬於不同的抽象化層次。如果被觀察者和觀察者都被扔到一起,那么這個對象必然跨越抽象化和具體化層次。
第二、觀察者模式支持廣播通訊。被觀察者會向所有的登記過的觀察者發出通知,
觀察者模式有下面的缺點:
第一、如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
第二、如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰。在使用觀察者模式是要特別注意這一點。
第三、如果對觀察者的通知是通過另外的線程進行異步投遞的話,系統必須保證投遞是以自恰的方式進行的。
第四、雖然觀察者模式可以隨時使觀察者知道所觀察的對象發生了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的對象是怎么發生變化的。
1.3 觀察者模式的應用場景:
1、 對一個對象狀態的更新,需要其他對象同步更新,而且其他對象的數量動態可變。
2、 對象僅需要將自己的更新通知給其他對象而不需要知道其他對象的細節。
1.4 如何實現:
現在我們舉一個類似的情況,並使用代碼來實現,為大家提供一個比較明顯的認識。
警察在找到嫌犯的時候,為了找到幕后主使,一般都會蹲點監察,這里我有三名便衣警察來蹲點監察2名嫌犯,三名便衣分別是:張昊天、石破天、趙日天,兩名嫌犯是:大熊與黑狗,詳見代碼:
觀察者接口:Observer
public interface Observer { void update(String message); }
定義便衣觀察者:police
public class Police implements Observer{ @Override public void update(String message) { System.out.println("有人偷東西了"); } }
目標接口:BadGuy
public interface BadGuy{ void notice(String message); }
定義壞人類:(注意壞人類里面 有 police的類)
public class Bad1 implements BadGuy{ private Police police=new Police(); @Override public void notice(String message) { police.update(message); } }
測試類:MainTest
public class MainTest { public static void main(String[] args) { BadGuy badGuy=new Bad1(); Observer observer=new Police(); String message="又賣了一批貨"; badGuy.notice(message); } }
測試結果:
有人偷東西了
通過上面的實例可以很明顯的看出,觀察者模式的大概模型,關鍵是什么呢?
關鍵點:
1、針對觀察者與被觀察者分別定義接口,有利於分別進行擴展。
2、重點就在被觀察者的實現中:
(1)定義觀察者集合,並定義針對集合的添加、刪除操作,用於增加、刪除訂閱者(觀察者)
(2)定義通知方法,用於將新情況通知給觀察者用戶(訂閱者用戶)
3、觀察者中需要有個接收被觀察者通知的方法。
如此而已!
觀察者模式定義的是一對多的依賴關系,一個被觀察者可以擁有多個觀察者,並且通過接口對觀察者與被觀察者進行邏輯解耦,降低二者的直接耦合。
如此這般,想了一番之后,突然發現這種模式與橋接模式有點類似的感覺。
橋接模式也是擁有雙方,同樣是使用接口(抽象類)的方式進行解耦,使雙方能夠無限擴展而互不影響,其實二者還是有者明顯的區別:
1、主要就是使用場景不同,橋接模式主要用於實現抽象與實現的解耦,主要目的也正是如此,為了雙方的自由擴展而進行解耦,這是一種多對多的場景。觀察者模式側重於另一方面的解耦,側重於監聽方面,側重於一對多的情況,側重於一方發生情況,多方能獲得這個情況的場景。
2、另一方面就是編碼方面的不同,在觀察者模式中存在許多獨有的內容,如觀察者集合的操作,通知的發送與接收,而在橋接模式中只是簡單的接口引用。
我感覺不太直觀,自己寫了個例子:
package com.sankuai.qcs.regulation.observerModel; public interface actionBad { void addActionBad(actionPolice observer); void notice(String message); }
package com.sankuai.qcs.regulation.observerModel; /** * @description: com.sankuai.qcs.regulation.observerModel * @author: yinfuqing@meituan.com * @date: 2019-04-23-17-51 **/ public interface actionPolice { void update(String message,String name); }
package com.sankuai.qcs.regulation.observerModel; import java.util.ArrayList; import java.util.List; /** * @description: com.sankuai.qcs.regulation.observerModel * @author: yinfuqing@meituan.com * @date: 2019-04-23-17-56 **/ public class Bad implements actionBad { private String name="大熊"; private List<actionPolice> actionList=new ArrayList<>(); @Override public void addActionBad(actionPolice observer) { if(!actionList.contains(observer)){ actionList.add(observer); } } @Override public void notice(String message) { for(actionPolice actionPolice:actionList){ actionPolice.update(message,name); } } }
package com.sankuai.qcs.regulation.observerModel; /** * @description: com.sankuai.qcs.regulation.observerModel * @author: yinfuqing@meituan.com * @date: 2019-04-23-17-59 **/ public class mainTest { public static void main(String[] args) { actionBad badGuy1=new Bad(); actionPolice o1=new Police(); badGuy1.addActionBad(o1); String message="又進了一批貨物"; badGuy1.notice(message); } }
package com.sankuai.qcs.regulation.observerModel; /** * @description: com.sankuai.qcs.regulation.observerModel * @author: yinfuqing@meituan.com * @date: 2019-04-23-17-52 **/ public class Police implements actionPolice { private String name1="警察1"; @Override public void update(String message, String name) { System.out.println("警察 "+name1+" : "+ name+" 那里有新情況 "+message); } }