观察者模式是一种平时接触较多的模式。它主要用于一对多的通知发布机制,当一个对象发生改变时自动通知其余对象,其余对象便作出相应的反应,同时保证了被观察对象与观察对象之间没有直接的依赖。设计模式
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#的工程化实现及扩展》