在開發中有時候須要實現一個對象A的變化而後更新另一個對象B編程
這個實現的最簡單的方式時在目標對象A的方法中添加B更新的邏輯代碼架構
可是咱們但願可以用一種比較優雅的方式實現,好比當需求變化時不須要改A的代碼,而且能夠隨時添加或者刪除處理函數。mvc
在大多數gui編程中,都會提供這個事件機制。異步
在網頁的頁面交互中,咱們能夠註冊本身的方法到輸入框的clikc或者change事件中,async
document.getElementByTag("input").addEventListener("click",fn);ide
document.getElementByTag("input").removeEventListener("click",fn);函數
//IE中方法使用attachEventui
這樣就能把fn處理方法綁定或者解綁到input的click事件中this
不過,經過這種方式綁定的事件event只能是對象自帶的spa
好比,只能註冊 「click」,"focus","blur","change","load","mouseout"等一些對象元素自帶的事件
若是咱們想給input添加一個自定義事件或者給自定義的panel添加事件處理程序的話,只能經過本身實現了。
下面來個實現
1 //輸入框變化,改變其餘對象 2 //事件插件,其餘對象可擴展此對象進行事件監控 3 var Events = (function () { 4 var events = {}; 5 return { 6 addListeners: function (fn, callback) { 7 events[fn] = events[fn] || []; 8 events[fn].push(callback); 9 }, 10 removeListeners: function (fn, callback) { 11 events[fn] = events[fn].filter(item => { 12 return item != callback; 13 }) 14 }, 15 fireEvent: function (fn, param) { 16 var args = Array.prototype.slice.call(arguments, 0), 17 me = this; 18 if (events[fn]) { 19 events[fn].forEach(item => { 20 item.call(this, param); 21 }) 22 } 23 } 24 } 25 })(); 26 27 //輸入組件,和他的兩個實例方法 28 function Input() { } 29 Input.prototype.userInput = function (param) { 30 this.fireEvent("change", param); 31 } 32 Input.prototype.userLeave = function (param) { 33 this.fireEvent("keyup", param); 34 } 35 36 //監控輸入組件,根據輸入框變化更新此面板 37 function Panel() { } 38 Panel.prototype.showClick = function (param) { 39 console.log("Panel show"); 40 } 41 Panel.prototype.hideClick = function (param) { 42 console.log("Panel hide"); 43 } 44 45 //實例化input對象,並繼承Events的屬性和方法 46 var input = new Input(); 47 Object.assign(input, Events); 48 49 var panel = new Panel(); 50 51 //註冊處理程序到change和keyup事件 52 input.addListeners("change", panel.showClick); 53 input.addListeners("keyup", panel.hideClick); 54 55 //經過實例方法觸發change和keyup事件 56 input.userInput(); //輸出結果:--Panel show 57 input.userLeave(); //輸出結果:--Panel hide
上面Events對象 是個簡易的事件綁定方法,全部其餘的對象均可以擴展此方法來實現事件的綁定。
C#用委託來實現事件綁定
1 using System; 2 3 namespace ConsoleApp2 4 { 5 6 public class Program 7 { 8 static void Main(string[] args) 9 { 10 var subject = new Subject(); 11 var observer = new Observer(subject); 12 subject.update(); 13 Console.ReadLine(); 14 15 } 16 } 17 18 public delegate void ChangeHandler(); 19 20 public class Subject 21 { 22 23 public ChangeHandler changeHandler; 24 public void update() 25 { 26 //if (changeHandler != null) 27 //{ 28 // changeHandler(); 29 //} 30 changeHandler?.Invoke(); 31 32 } 33 } 34 35 public class Observer 36 { 37 public Observer(Subject subject) 38 { 39 subject.changeHandler += new ChangeHandler(change); 40 } 41 public void change() 42 { 43 Console.WriteLine("subject had updated"); 44 } 45 } 46 }
經過一個對象改變從而引發其餘對象變化的狀況,在gof書籍中稱爲觀察者模式,或者叫發佈-訂閱模式 ,屬於對象行爲型模式
意圖
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於他的對象都獲得通知並被自動更新
適用性
適用於如下的任一種狀況
下面來實現這個模型
咱們把這個狀態變化的對象成爲目標對象(Subject),根據目標對象變化而變化的對象成爲觀察者(Observers)
目標對象
1 //目標對象基類 2 public abstract class BasSubject 3 { 4 //觀察者對象 5 public List<BasObserver> observerList = new List<BasObserver>(); 6 //狀態 7 public string State 8 { 9 get; set; 10 } 11 /// <summary> 12 /// 附加觀察者 13 /// </summary> 14 /// <param name="observer"></param> 15 public void attachObserver(BasObserver observer) 16 { 17 observerList.Add(observer); 18 } 19 /// <summary> 20 /// 移除觀察者 21 /// </summary> 22 /// <param name="observer"></param> 23 public void detachObserver(BasObserver observer) 24 { 25 observerList.Remove(observer); 26 } 27 /// <summary> 28 /// 狀態變動 29 /// </summary> 30 public virtual void onChangeState() 31 { 32 observerList.ForEach(async item => 33 { 34 await item.update(); 35 }); 36 37 } 38 39 } 40 // 子類 41 public class Subject:BasSubject 42 { 43 public void onChangeState(string state) 44 { 45 State = state; 46 base.onChangeState(); 47 } 48 }
觀察者對象
1 //觀察者抽象類或者接口 2 public abstract class BasObserver 3 { 4 public BasSubject subject; 5 public BasObserver() { 6 7 } 8 public BasObserver(BasSubject subject) { 9 this.subject = subject; 10 this.subject.attachObserver(this); 11 } 12 public virtual async Task update() { 13 } 14 } 15 //對象A 16 public class ObserverA : BasObserver 17 { 18 BasSubject subject; 19 public ObserverA() 20 { 21 22 } 23 public ObserverA(BasSubject subject) : base(subject) 24 { 25 this.subject = subject; 26 } 27 public override async Task update() 28 { 29 if (this.subject.State == "interest") 30 { 31 await Task.Run(() => 32 { 33 Thread.Sleep(1000); 34 }); 35 Console.WriteLine("interest change---from{0}", this.GetType()); 36 } 37 38 } 39 } 40 //對象B 41 public class ObserverA : BasObserver 42 { 43 BasSubject subject; 44 public ObserverA() 45 { 46 47 } 48 public ObserverA(BasSubject subject) : base(subject) 49 { 50 this.subject = subject; 51 } 52 public override async Task update() 53 { 54 if (this.subject.State == "interest") 55 { 56 await Task.Run(() => 57 { 58 Thread.Sleep(1000); 59 }); 60 Console.WriteLine("interest change---from{0}", this.GetType()); 61 } 62 63 } 64 }
執行
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Subject subject = new Subject(); 6 ObserverA observerA1 = new ObserverA(subject); 7 ObserverA observerA2 = new ObserverA(); 8 ObserverB observerB = new ObserverB(subject); 9 10 subject.onChangeState("interest"); 11 Console.WriteLine(); 12 subject.onChangeState("un interest"); 13 Console.ReadLine(); 14 } 15 }
結果
該模式可實現獨立的添加觀察者,而無需修改目標對象和其餘觀察者對象。目標和觀察者之間是經過抽象耦合的,觀察者只須要實現指定的接口,即可獲得目標對象的更新。
可改進的說明
應用場景
觀察者模式最先應用於malltalk的mvc架構中。
其中model爲目標對象,view爲觀察者,一個model可能會綁定到多個視圖中,當model變化時須要同時改變各個view。
在項目開發中,咱們提倡職責分離,因此咱們能夠添加一個controller層來處理響應用戶輸入,更新模型等功能
針對上面的例子加入控制器的代碼以下
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var observer = new Observer(); 6 observer.submit(); 7 Console.ReadLine(); 8 } 9 } 10 11 public class Controller { 12 Subject subject; 13 Observer observer; 14 /// <summary> 15 /// 註冊觀察者到目標 16 /// </summary> 17 /// <param name="observer"></param> 18 public Controller(Observer observer) { 19 this.observer = observer; 20 this.subject = new Subject(); 21 subject.attachObserver(observer); 22 } 23 /// <summary> 24 /// 處理請求 25 /// </summary> 26 public void doSomeThing () { 27 subject.State = "interest"; 28 subject.onChangeState(); 29 } 30 31 } 32 public class Subject { 33 List<Observer> list; 34 public Subject() { 35 list = new List<Observer>(); 36 } 37 public string State { get; set; } 38 public void attachObserver(Observer v) 39 { 40 list.Add(v); 41 } 42 public void onChangeState() { 43 list.ForEach(item => 44 { 45 item.update(this.State); 46 }); 47 } 48 } 49 public class Observer { 50 Controller c; 51 public Observer() { 52 c = new Controller(this); 53 } 54 /// <summary> 55 /// 用戶操做 56 /// </summary> 57 public void submit() { 58 c.doSomeThing(); 59 } 60 /// <summary> 61 /// 刷新 62 /// </summary> 63 /// <param name="label"></param> 64 public void update(string label) { 65 Console.WriteLine(label); 66 } 67 68 }
在控制器中實現目標對象到觀察者之間的映射。控制器收到視圖發來的請求,去更新模型。模型變化會自動更新視圖層