當對象間存在一對多關系時,則使用觀察者模式(Observer Pattern)逞频。比如,當一個對象被修改時栋齿,則會自動通知它的依賴對象苗胀。觀察者模式屬于行為型模式。
介紹
意圖:定義對象間一種一對多的依賴關系瓦堵,當一個對象的狀態(tài)發(fā)生改變時基协,所有依賴于它的對象都得到通知并自動更新。
主要解決:一個對象狀態(tài)改變給其他對象通知的問題菇用,而且要考慮到易用和低耦合澜驮,保證高度的寫作。
何時使用:一個對象(目標對象)的狀態(tài)發(fā)生改變惋鸥,所有的依賴對象(觀察者對象)都將得到通知杂穷,進行廣播通知。
關鍵代碼:在抽象類里有一個ArrayList存放著觀察者們卦绣。
優(yōu)點:1.觀察者和被觀察者是抽象耦合的耐量。2.建立一套觸發(fā)機制。
缺點:1.如果一個被觀察者對象有很多的直接和間接的觀察者的話滤港,將所有的觀察者都通知到會花費很多時間拴鸵。2.如果在觀察者和觀察目標之間有循環(huán)依賴的話,觀察目標會觸發(fā)它們之間進行循環(huán)使用蜗搔,可能導致系統(tǒng)崩潰。3.觀察者模式?jīng)]有相應的機制讓觀察者知道所觀察的目標對象時怎么發(fā)生的八堡,而僅僅只是知道觀察目標發(fā)生了變化樟凄。
使用場景:
- 一個抽象模型有兩個方面,其中一個方面依賴于另一個方面兄渺。將這些方面封裝在獨立的對象中使它們可以各自獨立的改變和復用缝龄。
- 一個對象的改變將導致其他一個或多個對象也發(fā)生改變,而不知道具體有多少對象將發(fā)生改變挂谍,可以降低對象之間的耦合度叔壤。
- 一個對象必須通知其他對象,而并不知道這些對象是誰口叙。
- 需要在系統(tǒng)中創(chuàng)建一個觸發(fā)鏈炼绘,A對象的行為將影響B(tài)對象,B對象的行為將影響C對象....妄田,可以使用觀察者模式創(chuàng)建一種鏈式觸發(fā)機制俺亮。
注意事項:1.java中已經(jīng)有了對觀察者模式的支持類驮捍。2.避免循環(huán)使用。3.如果順序執(zhí)行脚曾,某一個觀察者錯誤會導致系統(tǒng)卡殼东且,一般采用異步方式。
Java實現(xiàn)
觀察者模式使用三個類Subject本讥、Observer和Client珊泳。Subject對象帶有綁定觀察者到Client對象和從Client對象解綁觀察者的方法。我們創(chuàng)建Subject類拷沸、Observer抽象類和擴展了抽象類Observer的實體類色查。
步驟一
創(chuàng)建Subject類。
// Subject.java
import java.util.ArrayList;
import java.util.List;
public class Subject {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer) {
observers.add(observer);
}
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
步驟二
創(chuàng)建Observer類
// Observer.java
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
步驟三
創(chuàng)建實體觀察類
// BinaryObserver.java
public class BinaryObserver extends Observer {
public BinaryObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Binary String:" + Integer.toBinaryString(subject.getState()));
}
}
// OctalObserver.java
public class OctalObserver extends Observer {
public OctalObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Octal String: " + INteger.toOctalString(subject.getState()));
}
}
// HexaObserver.java
public class HexaObserver extends Observer {
public HexaObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Hex String: " + Integer.toHexString(subject.getState()).toUpperCase());
}
}
步驟四
使用subject和實體觀察者對象
// ObserverPatternDemo.java
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First State change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
步驟5
執(zhí)行程序堵漱,輸出結果
First state change : 15
Hex String: F
Octal String: 17
Binary String: 1111
Second state change : 10
Hex String: A
Octal String: 12
Binary String: 010
ES6下javascript的實現(xiàn)
// 聲明EventEmitter時間發(fā)生器構造函數(shù)
function EventEmitter() {
this._events = Object.create(null);
}
// 設置最大監(jiān)聽數(shù)
EventEmitter.prototype.setMaxListeners = function(n) {
this._count = n;
}
// 得到最大監(jiān)聽數(shù)
EventEmitter.prototype.getMaxListeners = function(n) {
return this._count ? this._count : EventEmitter.defaultMaxListeners;
}
// eventName方法
EventEmitter.prototype.eventNames = function() {
return Object.keys(this._events);
}
// on 訂閱方法的實現(xiàn)综慎,跟addListener相同
EventEmitter.prototype.adddListener = EventEmitter.prototype.on = function(type, callback, flag) {
// 如果實例不存在則創(chuàng)建一個空對象,Object.create(null)沒有鏈
if (!this._events) {
this._events = Object.create(null);
}
// 不是newListener 就應該讓newListener執(zhí)行下
if (type !== "newListener") {
if (this._events["newListener"]) {
this._events["newListener"].forEach(fn => fn(type))
}
}
// flag 標識
if (this._events[type]) {
if (flag) {
this._events[type].unshift(callback);
} else {
this._events[type].push(callback);
}
} else {
// 內(nèi)部未存放
this._events[type] = [callback];
}
if (this._events[type].length === this.getMaxListeners()) {
console.warn('內(nèi)存泄露');
}
};
// once 實現(xiàn)
EventEmitter.prototype.once = function(type, callback, flag) {
let one = (...args) => {
callback(...args);
this.removeListener(type, one);
}
// 自定義屬性 因為實例中沒有wrap屬性
one.l = callback;
this.on(type, one, flag);
};
// prependListener方法實現(xiàn)
EventEmitter.prototype.prependOnceListener = function(type,callback){
this.once(type,callback,true);
};
// emit方法實現(xiàn)
EventEmitter.prototype.emit = function(type,...args){
if(this._events[type]){ //{失戀:[cry,eat]} 如果失戀對應有值勤庐,依次執(zhí)行里面的方法
this._events[type].forEach(fn=>fn(...args))
}
};
// 移除訂閱事件的方法
EventEmitter.prototype.removeListener = function(type,callback){
if(this._events[type]){
// 返回false就表示不要了,用filter實現(xiàn)去重
this._events[type] = this._events[type].filter(fn=>fn!==callback && fn.l!==callback)
}
};
// removeAllListeners 移除所有的監(jiān)聽者
EventEmitter.prototype.removeAllListeners = function(){
this._events = Object.create(null);
};
// listeners 方法
EventEmitter.prototype.listeners = function(type){
return this._events[type];
};
module.exports = EventEmitter;