前言
本來沒想寫前言的,感覺就是一堆廢話。那就當廢話瀏覽一下吧,只是提醒一下自己一些注意的東西。
在許多博客當中看到把觀察者模式又稱呼爲發佈-訂閱模式。其實我感覺兩者確實很相似,但還是有一丟丟的區別:
在發佈-訂閱模式中消息的發送方,叫做發佈者(publishers),消息不會直接發送給特定的接收者(訂閱者)。
舉個例子:比如微信公衆號。意思就是發佈者和訂閱者不知道對方的存在,需要一個第三方組件,叫做信息中介,它將訂閱者和發佈者串聯起來,它過濾和分配所有輸入的消息。(這段從網上找的,大家可以從網上詳細瞭解發佈-訂閱模式)這裏就不跑題了。
一、簡介
1、屬於行爲型模式:這些設計模式特別關注對象之間的通信。
2、當對象間存在一對多關係時,則使用觀察者模式。比如,當一個對象被修改時,則會自動通知它的依賴對象。
3、意圖:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
4、主要解決:一個對象狀態改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協作。
5、何時使用:一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知。
6、使用場景:
(1)一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。將這些方面封裝在獨立的對象中使它們可以各自獨立地改變和複用。
(2)一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,可以降低對象之間的耦合度。
(3)一個對象必須通知其他對象,而並不知道這些對象是誰。
(4)需要在系統中創建一個觸發鏈,A對象的行爲將影響B對象,B對象的行爲將影響C對象……,可以使用觀察者模式創建一種鏈式觸發機制。
7、注意事項:
(1)JAVA 中已經有了對觀察者模式的支持類。
(2)避免循環引用。
(3)如果順序執行,某一觀察者錯誤會導致系統卡殼,一般採用異步方式。
二、實現步驟
(1)實現方式:
實現觀察者模式有很多形式,比較直觀的一種是使用一種“註冊——通知——註銷”的形式。
比如Android中的廣播(不懂android也沒關係,把它想象成學校的廣播就成,具體看下面例子)
觀察者模式主要角色
抽象觀察者:描述觀察者的公共接口(收到消息的方法),也可以定義爲接口(interface)。
具體觀察者:描述具體觀察者並對觀察目標的改變做出反應。
抽象被觀察者(目標):是指被觀察的對象,描述被觀察者的公共接口(比如通知、註冊、註銷等),也可以定義爲接口
具體被觀察者(具體目標):描述具體的被觀察者,與觀察者建立聯繫。當狀態發生變化時,通知觀察者。
(2)舉例:
這裏舉個例子:智商有點不夠用,想不到好的例子,這裏就模擬Android四大組件之一的廣播實現方式。
首先我們要知道Android廣播的大致實現流程:創建接收者(具體觀察者)繼承廣播(抽象觀察者),然後註冊廣播(綁定觀察者,建立聯繫)。廣播發送者(具體的被觀察者)發送廣播通知。最後是註銷廣播(解除綁定,斷開聯繫)。
看起來有點複雜,換種思路簡單的想象成學校發出廣播通知學生放學即可。
(3)步驟簡化版:
1、創建具體被觀察者(學校廣播)繼承抽象 被觀察者
2、創建具體觀察者(學生)繼承抽象觀察者
3、綁定聯繫
4、發送通知
哈哈 ~!相信到這裏大家可以自己手動了實現了,下面貼出具體代碼。
三、代碼實現
抽象 被觀察者:Observable.java;具體 被觀察者:SchoolsBroadcast.java
/**
* Observable.java
* 抽象被觀察者
*/
abstract class Observable {
//發送廣播
abstract void sendBroadcast(String message);
}
/**
* SchoolsBroadcast.java
* 具體的 被觀察者(學校廣播)
*/
public class SchoolsBroadcast extends Observable{
//用來存儲觀察者
private List<Observer> observers = new ArrayList<Observer>();
@Override
void sendBroadcast(String message) {
System.out.println("學校發出通知:"+message);
for(Observer ob:observers) {
ob.receive(message);
}
}
//綁定觀察者(可以移動到抽象被觀察者中)
public void registerReceiver(Observer observer) {
observers.add(observer);
}
//解綁觀察者(可以移動到抽象被觀察者中)
public void unRegisterReceiver(Observer observer) {
if(observers.contains(observer)) {
observers.remove(observer);
}
}
}
抽象 觀察者:Observer.java;具體 觀察者:StudentA.java、StudentB.java、StudentC.java
/**
* Observer.java
* 抽象觀察者
*/
abstract class Observer {
//收到通知
abstract void receive(String message);
}
/**
* StudentA.java
* 具體的觀察者(學生A)
*/
public class StudentA extends Observer{
@Override
void receive(String message) {
System.out.println("學生A收到消息:"+message);
}
}
/**
* StudentB.java
* 具體的觀察者(學生B)
*/
public class StudentB extends Observer{
@Override
void receive(String message) {
System.out.println("學生B收到消息:"+message);
}
}
/**
* StudentC.java
* 具體的觀察者(學生C)
*/
public class StudentC extends Observer{
@Override
void receive(String message) {
System.out.println("學生C收到消息:"+message);
}
}
測試類:Test.java
/**
* Test.java
* 測試類
*/
public class Test {
public static void main(String[] args) {
//創建被觀察者(學校廣播)
SchoolsBroadcast schoolsBroadcast = new SchoolsBroadcast();
//創建觀察者(學生)
StudentA studentA = new StudentA();
//綁定觀察者 建立聯繫
schoolsBroadcast.registerReceiver(studentA);
schoolsBroadcast.registerReceiver(new StudentB());
schoolsBroadcast.registerReceiver(new StudentC());
//被觀察者(學校廣播)發出通知
schoolsBroadcast.sendBroadcast("放學");
System.out.println("=====================");
//解綁觀察者(學生A)
schoolsBroadcast.unRegisterReceiver(studentA);
//被觀察者(學校廣播)發出通知
schoolsBroadcast.sendBroadcast("學生A請假");
}
}
例子很簡單、容易忘的可以多寫幾遍,很容易記住。當然面試時還是賊好用滴!!!
四、總結
優點:
1、觀察者和被觀察者是抽象耦合的。
2、建立一套觸發機制。.
3、符合“開閉原則”的要求。
缺點:
1、如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
2、如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
3、觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。
五、Demo地址
https://github.com/DayorNight/DesignPattern
六、參考文檔
http://www.runoob.com/design-pattern/observer-pattern.html
七、內容推薦
簡書:
相關文章:
如果你覺得我寫的不錯或者對您有所幫助的話。不妨頂一個【微笑】,別忘了點贊、收藏、加關注哈!!
您的每個舉動都是對我莫大的支持