观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使观察者对象它们能够自动更新自己。
说白了就是说一个对象,能够通过另外一个对象(通知者)的状态改变自身的状态。
问:什么时候应该用观察者模式呢?
答:当一个对象的改变需要同时改变其他对象的时候。
观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
下面给一个观察者模式的UML图:
下面给出观察者模式的代码结构:
namespace ConsoleApplication1 { //Subject类,可翻译为抽象通知者 abstract class Subject { private IList<Observer> observers = new List<Observer>(); //增加观察者 public void Attach(Observer observer) { observers.Add(observer); } //移除观察者 public void Detach(Observer observer) { observers.Remove(observer); } //通知 public void Notify() { foreach (Observer o in observers) { o.Update(); } } } //抽象观察者 abstract class Observer { public abstract void Update(); } //ConcreteSubject类,具体通知者 class ConcreteSubject : Subject { private string subjectState; //具体被观察者状态 public string SubjectState { get { return subjectState; } set { subjectState = value; } } } //ConcreteObserver类,具体观察者 class ConcreteObserver : Observer { private string name; private string observerState; private ConcreteSubject subject; public ConcreteObserver(ConcreteSubject subject, string name) { this.subject = subject; this.name = name; } public override void Update() { observerState = subject.SubjectState; Console.WriteLine("观察者{0}的新状态是{1}",name,observerState); } public ConcreteSubject Subject { get { return subject; } set { subject = value; } } } class Program { static void Main(string[] args) { ConcreteSubject s = new ConcreteSubject(); s.Attach(new ConcreteObserver(s, "X")); s.Attach(new ConcreteObserver(s, "Y")); s.Attach(new ConcreteObserver(s, "Z")); s.SubjectState = "ABC"; s.Notify(); Console.ReadKey(); } } }
现在回到《大话设计模式》里面的例子老板回来了的例子:
namespace ConsoleApplication1 { //通知者接口 interface Subject { void Attach(Observer observer); void Detach(Observer observer); void Notify(); string SubjectState { get; set; } } //老板通知者,如果老板没被发现,那么老板进来的,就相当于通知员工老板回来了 class Boss : Subject { //同事列表 private IList<Observer> observers = new List<Observer>(); private string action; //增加 public void Attach(Observer observer) { observers.Add(observer); } //减少 public void Detach(Observer observer) { observers.Remove(observer); } //通知 public void Notify() { foreach (Observer o in observers) { o.Update(); } } //老板状态 public string SubjectState { get { return action; } set { action = value; } } } //秘书通知者,如果老板没被发现,那么老板进来的,就相当于通知员工老板回来了 class Secretary : Subject { //同事列表 private IList<Observer> observers = new List<Observer>(); private string action; //增加 public void Attach(Observer observer) { observers.Add(observer); } //减少 public void Detach(Observer observer) { observers.Remove(observer); } //通知 public void Notify() { foreach (Observer o in observers) { o.Update(); } } //老板状态 public string SubjectState { get { return action; } set { action = value; } } } //抽象观察者 abstract class Observer { protected string name; protected Subject sub; public Observer(string name, Subject sub) { this.name = name; this.sub = sub; } public abstract void Update(); //模拟对象状态的改变 } //看股票的同事 class StockObserver : Observer { public StockObserver(string name, Subject sub) : base(name,sub) { } public override void Update() //模拟对象状态的改变 { Console.WriteLine("{0}{1}关闭股票行情,继续工作!", sub.SubjectState, name); } } //看NBA的同事 class NBAObserver : Observer { public NBAObserver(string name, Subject sub) : base(name, sub) { } public override void Update() //模拟对象状态的改变 { Console.WriteLine("{0}{1}关闭NBA直播,继续工作!", sub.SubjectState, name); } } class Program { static void Main(string[] args) { //老板 Boss LaoBan = new Boss(); //看股票的同事 StockObserver tongshi1 = new StockObserver("魏关宅",LaoBan); //看NBA的同事 NBAObserver tongshi2 = new NBAObserver("易观察", LaoBan); LaoBan.Attach(tongshi1); LaoBan.Attach(tongshi2); LaoBan.Detach(tongshi1); //老板回来了 LaoBan.SubjectState = "我老板回来了"; //调用通知方法 发出通知 LaoBan.Notify(); //秘书 Secretary secretary = new Secretary(); //看股票的同事 StockObserver tongshi3 = new StockObserver("魏关宅", secretary); //看NBA的同事 NBAObserver tongshi4 = new NBAObserver("易观察", secretary); secretary.Attach(tongshi3); secretary.Attach(tongshi4); //老板回来了 secretary.SubjectState = "我是秘书,老板回来了"; //调用方法,发出通知 secretary.Notify(); Console.ReadKey(); } } }
其实事件委托也能够实现观察者模式,而且还好,例如当一个观察者代码已经写好了之后,用委托可以再不用改动已经写好具体的观察者代码就实现观察者模式。以下给出用委托实现的观察者模式代码:
namespace ConsoleApplication1 { //通知者接口 interface Subject { void Notify(); string SubjectState { get; set; } } //声明一个类外委托 delegate void EventHandler(); //老板通知者,如果老板没被发现,那么老板进来的,就相当于通知员工老板回来了 class Boss : Subject { //声明一事件Update,类型为委托EventHandler public event EventHandler Update; private string action; //通知 public void Notify() { Update(); //在访问通知方法时,调用"更新" } //老板状态 public string SubjectState { get { return action; } set { action = value; } } } //秘书通知者,如果老板没被发现,那么老板进来的,就相当于通知员工老板回来了 class Secretary : Subject { //声明一事件Update,类型为委托EventHandler public event EventHandler Update; private string action; //通知 public void Notify() { Update(); } //老板状态 public string SubjectState { get { return action; } set { action = value; } } } //看股票的同事 class StockObserver { private string name; private Subject sub; public StockObserver(string name, Subject sub) { this.name = name; this.sub = sub; } public void CloseStock() //模拟对象状态的改变 { Console.WriteLine("{0}{1}关闭股票行情,继续工作!", sub.SubjectState, name); } } //看NBA的同事 class NBAObserver { private string name; private Subject sub; public NBAObserver(string name, Subject sub) { this.name = name; this.sub = sub; } public void CloseNBA() //模拟对象状态的改变 { Console.WriteLine("{0}{1}关闭NBA直播,继续工作!", sub.SubjectState, name); } } class Program { static void Main(string[] args) { Boss LaoBan = new Boss(); //老板 StockObserver tongshi1 = new StockObserver("魏关宅", LaoBan); //看股票的同事 NBAObserver tongshi2 = new NBAObserver("易观察", LaoBan); //看NBA的同事 LaoBan.SubjectState = "我老板回来了"; //老板回来了 LaoBan.Update += new EventHandler(tongshi1.CloseStock); //将关闭股票的方法挂钩到老板的Update LaoBan.Update += new EventHandler(tongshi2.CloseNBA); LaoBan.Notify(); //调用通知方法 发出通知 Console.ReadKey(); } } }
输出结果如图所示: