觀察者模式是一種平時接觸較多的模式。它主要用於一對多的通知發佈機制,當一個對象發生改變時自動通知其餘對象,其餘對象便作出相應的反應,同時保證了被觀察對象與觀察對象之間沒有直接的依賴。設計模式
GOF對觀察者模式的描述爲:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically..
— Design Patterns : Elements of Reusable Object-Oriented Software網絡
UML類圖以下:
測試
代碼實例this
public interface IObserver<T> { void Update(SubjectBase<T> subject); } public abstract class SubjectBase<T> { protected IList<IObserver<T>> observers = new List<IObserver<T>>(); protected T state; public virtual T State { get { return state; } } //Attach public static SubjectBase<T> operator +(SubjectBase<T> subject, IObserver<T> observer) { subject.observers.Add(observer); return subject; } //Detach public static SubjectBase<T> operator -(SubjectBase<T> subject, IObserver<T> observer) { subject.observers.Remove(observer); return subject; } //更新各觀察者 public virtual void Notify() { foreach (var observer in observers) { observer.Update(this); } } public virtual void Update(T state) { this.state = state; Notify();//觸發對外通知 } } public class Subject<T> : SubjectBase<T> { } public class Observer<T> : IObserver<T> { public T State; public void Update(SubjectBase<T> subject) { this.State = subject.State; } }
調用端設計
static void Main(string[] args) { SubjectBase<int> subject = new Subject<int>(); Observer<int> observer1 = new Observer<int>(); observer1.State = 10; Observer<int> observer2 = new Observer<int>(); observer2.State = 20; subject += observer1; subject += observer2; subject.Update(30); Console.WriteLine($"ob1:{observer1.State} ob2:{observer2.State}"); //ob1:30 ob2:30 兩個觀察者都發生了變化 subject -= observer2; subject.Update(40); Console.WriteLine($"ob1:{observer1.State} ob2:{observer2.State}"); //ob1:40 ob2:30 observer2被移除,不會跟隨變化 }
這裏的被觀察者繼承基類SubjectBase,觀察者實現接口IObserver。SubjectBase和IObserver相互依賴,SubjectBase自己不知道會有哪些具體IObserver類型但願得到它的更新通知,具體的Observer類型也並不須要關心目標類型,只須要依賴SubjectcBase,因此實際上一個觀察者能夠跟蹤多個被觀察者。調試
根據當目標對象狀態更新的時候,觀察者更新本身數據的方式,能夠將觀察者模式分爲推模式和拉模式。code
推模式:目標對象在通知裏把須要更新的信息做爲參數提供給IObserver的Update()方法。採用這種方式,觀察者只能只能被動接受,若是推送的內容比較多,那麼對網絡、內存或者I/O的開銷就會很大。server
拉模式:目標對象僅僅告訴觀察者有新的狀態,至於該狀態是什麼,則須要觀察者主動訪問目標對象來獲取。這種方式下,觀察者獲取信息的時機和內容均可以自主決定,但若是觀察者沒有及時獲取信息,就會漏掉以前通知的內容。對象
前面的代碼示例是兩種方式的結合,看起來像是推模式,但他推送的是一個SubjectBase的引用,觀察者能夠根據須要經過這個引用訪問到具體的狀態,從這個角度看又是拉模式。blog
.NET中的事件機制也能夠看做觀察者模式,事件所定義的委託類型自己就是個抽象的觀察者,並且相對經典的觀察者模式,事件更加簡單、靈活,耦合也更加鬆散。
代碼示例
public class UserEventArgs : EventArgs { public string Name { get; } public UserEventArgs(string name) { this.Name = name; } } public class User { public event EventHandler<UserEventArgs> NameChanged; private string name; public string Name { get { return name; } set { name = value; NameChanged(this, new UserEventArgs(value)); } } }
觀察者,註冊事件
public class Test { public static void Entry() { User user = new User(); user.NameChanged += (sender, args) => { Console.WriteLine(args.Name); }; user.Name = "Andy"; } }
參考書籍: 王翔著 《設計模式——基於C#的工程化實現及擴展》