前言
本來沒想寫前言的爽篷,感覺就是一堆廢話。那就當(dāng)廢話瀏覽一下吧趋艘,只是提醒一下自己一些注意的東西。
在許多博客當(dāng)中看到把觀察者模式又稱呼為發(fā)布-訂閱模式凶朗。其實(shí)我感覺兩者確實(shí)很相似瓷胧,但還是有一丟丟的區(qū)別:
在發(fā)布-訂閱模式中消息的發(fā)送方,叫做發(fā)布者(publishers)棚愤,消息不會(huì)直接發(fā)送給特定的接收者(訂閱者)搓萧。
舉個(gè)例子:比如微信公眾號(hào)杂数。意思就是發(fā)布者和訂閱者不知道對(duì)方的存在,需要一個(gè)第三方組件瘸洛,叫做信息中介揍移,它將訂閱者和發(fā)布者串聯(lián)起來,它過濾和分配所有輸入的消息反肋。(這段從網(wǎng)上找的那伐,大家可以從網(wǎng)上詳細(xì)了解發(fā)布-訂閱模式)這里就不跑題了。
一石蔗、簡介
1罕邀、屬于行為型模式:這些設(shè)計(jì)模式特別關(guān)注對(duì)象之間的通信。
2养距、當(dāng)對(duì)象間存在一對(duì)多關(guān)系時(shí)诉探,則使用觀察者模式。比如棍厌,當(dāng)一個(gè)對(duì)象被修改時(shí)肾胯,則會(huì)自動(dòng)通知它的依賴對(duì)象。
3耘纱、意圖:定義對(duì)象間的一種一對(duì)多的依賴關(guān)系敬肚,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新揣炕。
4、主要解決:一個(gè)對(duì)象狀態(tài)改變給其他對(duì)象通知的問題东跪,而且要考慮到易用和低耦合畸陡,保證高度的協(xié)作。
5虽填、何時(shí)使用:一個(gè)對(duì)象(目標(biāo)對(duì)象)的狀態(tài)發(fā)生改變丁恭,所有的依賴對(duì)象(觀察者對(duì)象)都將得到通知,進(jìn)行廣播通知斋日。
6牲览、使用場景:
(1)一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面依賴于另一個(gè)方面恶守。將這些方面封裝在獨(dú)立的對(duì)象中使它們可以各自獨(dú)立地改變和復(fù)用第献。
(2)一個(gè)對(duì)象的改變將導(dǎo)致其他一個(gè)或多個(gè)對(duì)象也發(fā)生改變,而不知道具體有多少對(duì)象將發(fā)生改變兔港,可以降低對(duì)象之間的耦合度庸毫。
(3)一個(gè)對(duì)象必須通知其他對(duì)象,而并不知道這些對(duì)象是誰衫樊。
(4)需要在系統(tǒng)中創(chuàng)建一個(gè)觸發(fā)鏈飒赃,A對(duì)象的行為將影響B(tài)對(duì)象利花,B對(duì)象的行為將影響C對(duì)象……展懈,可以使用觀察者模式創(chuàng)建一種鏈?zhǔn)接|發(fā)機(jī)制春锋。
7侮措、注意事項(xiàng):
(1)JAVA 中已經(jīng)有了對(duì)觀察者模式的支持類雷酪。
(2)避免循環(huán)引用隅居。
(3)如果順序執(zhí)行蟀架,某一觀察者錯(cuò)誤會(huì)導(dǎo)致系統(tǒng)卡殼重挑,一般采用異步方式踊餐。
二藕漱、實(shí)現(xiàn)步驟
(1)實(shí)現(xiàn)方式:
實(shí)現(xiàn)觀察者模式有很多形式欲侮,比較直觀的一種是使用一種“注冊——通知——注銷”的形式。
比如Android中的廣播(不懂a(chǎn)ndroid也沒關(guān)系肋联,把它想象成學(xué)校的廣播就成威蕉,具體看下面例子)
觀察者模式主要角色
抽象觀察者:描述觀察者的公共接口(收到消息的方法),也可以定義為接口(interface)橄仍。
具體觀察者:描述具體觀察者并對(duì)觀察目標(biāo)的改變做出反應(yīng)韧涨。
抽象被觀察者(目標(biāo)):是指被觀察的對(duì)象,描述被觀察者的公共接口(比如通知侮繁、注冊虑粥、注銷等),也可以定義為接口
具體被觀察者(具體目標(biāo)):描述具體的被觀察者宪哩,與觀察者建立聯(lián)系娩贷。當(dāng)狀態(tài)發(fā)生變化時(shí),通知觀察者锁孟。
(2)舉例:
這里舉個(gè)例子:智商有點(diǎn)不夠用彬祖,想不到好的例子,這里就模擬Android四大組件之一的廣播實(shí)現(xiàn)方式品抽。
首先我們要知道Android廣播的大致實(shí)現(xiàn)流程:創(chuàng)建接收者(具體觀察者)繼承廣播(抽象觀察者)储笑,然后注冊廣播(綁定觀察者,建立聯(lián)系)圆恤。廣播發(fā)送者(具體的被觀察者)發(fā)送廣播通知突倍。最后是注銷廣播(解除綁定,斷開聯(lián)系)盆昙。
看起來有點(diǎn)復(fù)雜羽历,換種思路簡單的想象成學(xué)校發(fā)出廣播通知學(xué)生放學(xué)即可。
(3)步驟簡化版:
1淡喜、創(chuàng)建具體被觀察者(學(xué)校廣播)繼承抽象 被觀察者
2窄陡、創(chuàng)建具體觀察者(學(xué)生)繼承抽象觀察者
3、綁定聯(lián)系
4拆火、發(fā)送通知
哈哈 ~跳夭!相信到這里大家可以自己手動(dòng)了實(shí)現(xiàn)了涂圆,下面貼出具體代碼。
三币叹、代碼實(shí)現(xiàn)
抽象 被觀察者:Observable.java润歉;具體 被觀察者:SchoolsBroadcast.java
/**
* Observable.java
* 抽象被觀察者
*/
abstract class Observable {
//發(fā)送廣播
abstract void sendBroadcast(String message);
}
/**
* SchoolsBroadcast.java
* 具體的 被觀察者(學(xué)校廣播)
*/
public class SchoolsBroadcast extends Observable{
//用來存儲(chǔ)觀察者
private List<Observer> observers = new ArrayList<Observer>();
@Override
void sendBroadcast(String message) {
System.out.println("學(xué)校發(fā)出通知:"+message);
for(Observer ob:observers) {
ob.receive(message);
}
}
//綁定觀察者(可以移動(dòng)到抽象被觀察者中)
public void registerReceiver(Observer observer) {
observers.add(observer);
}
//解綁觀察者(可以移動(dòng)到抽象被觀察者中)
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
* 具體的觀察者(學(xué)生A)
*/
public class StudentA extends Observer{
@Override
void receive(String message) {
System.out.println("學(xué)生A收到消息:"+message);
}
}
/**
* StudentB.java
* 具體的觀察者(學(xué)生B)
*/
public class StudentB extends Observer{
@Override
void receive(String message) {
System.out.println("學(xué)生B收到消息:"+message);
}
}
/**
* StudentC.java
* 具體的觀察者(學(xué)生C)
*/
public class StudentC extends Observer{
@Override
void receive(String message) {
System.out.println("學(xué)生C收到消息:"+message);
}
}
測試類:Test.java
/**
* Test.java
* 測試類
*/
public class Test {
public static void main(String[] args) {
//創(chuàng)建被觀察者(學(xué)校廣播)
SchoolsBroadcast schoolsBroadcast = new SchoolsBroadcast();
//創(chuàng)建觀察者(學(xué)生)
StudentA studentA = new StudentA();
//綁定觀察者 建立聯(lián)系
schoolsBroadcast.registerReceiver(studentA);
schoolsBroadcast.registerReceiver(new StudentB());
schoolsBroadcast.registerReceiver(new StudentC());
//被觀察者(學(xué)校廣播)發(fā)出通知
schoolsBroadcast.sendBroadcast("放學(xué)");
System.out.println("=====================");
//解綁觀察者(學(xué)生A)
schoolsBroadcast.unRegisterReceiver(studentA);
//被觀察者(學(xué)校廣播)發(fā)出通知
schoolsBroadcast.sendBroadcast("學(xué)生A請(qǐng)假");
}
}
例子很簡單、容易忘的可以多寫幾遍贩汉,很容易記住驱富。當(dāng)然面試時(shí)還是賊好用滴!Fノ琛褐鸥!
四、總結(jié)
優(yōu)點(diǎn):
1赐稽、觀察者和被觀察者是抽象耦合的叫榕。
2、建立一套觸發(fā)機(jī)制姊舵。.
3晰绎、符合“開閉原則”的要求。
缺點(diǎn):
1括丁、如果一個(gè)被觀察者對(duì)象有很多的直接和間接的觀察者的話荞下,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。
2史飞、如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話尖昏,觀察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰祸憋。
3会宪、觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對(duì)象是怎么發(fā)生變化的肖卧,而僅僅只是知道觀察目標(biāo)發(fā)生了變化蚯窥。
五、Demo地址
https://github.com/DayorNight/DesignPattern
六塞帐、參考文檔
http://www.runoob.com/design-pattern/observer-pattern.html
七拦赠、內(nèi)容推薦
CSDN:https://blog.csdn.net/cs_lwb/article/details/84189465
相關(guān)文章:
如果你覺得我寫的不錯(cuò)或者對(duì)您有所幫助的話。不妨頂一個(gè)【微笑】葵姥,別忘了點(diǎn)贊荷鼠、收藏、加關(guān)注哈@菩摇允乐!
您的每個(gè)舉動(dòng)都是對(duì)我莫大的支持
?