观察者模式——定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生改变时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式正如以上定义所说的,为了解决一个对象的状态改变,同时去改变另外几个对象的状态的一个设计模式。当然,那些观察者必须保持一致。
1.Subject类,抽象通知者类,定义并且实现了增加观察者,移除观察者,和通知观察者的方法。
1 /// <summary> 2 /// 抽象通知者 3 /// </summary> 4 public abstract class Subject 5 { 6 //定义一个观察者对象 7 private readonly IList<Observer> _observers = new List<Observer>(); 8 9 /// <summary> 10 /// 增加观察者 11 /// </summary> 12 /// <param name="observer">观察者</param> 13 public void Attach(Observer observer) 14 { 15 _observers.Add(observer); 16 } 17 18 /// <summary> 19 /// 移除观察者 20 /// </summary> 21 /// <param name="observer">观察者</param> 22 public void Detach(Observer observer) 23 { 24 _observers.Remove(observer); 25 } 26 27 /// <summary> 28 /// 进行通知 29 /// </summary> 30 public void Notify() 31 { 32 foreach (var observer in _observers) 33 { 34 observer.Update(); 35 } 36 } 37 }
2.ConcreteSubject类,定义了通知者的状态属性
1 /// <summary> 2 /// 具体通知者 3 /// </summary> 4 public class ConcreteSubject : Subject 5 { 6 //被观察的状态 7 public string SubjectState { get; set; } 8 }
3.Observer类,抽象观察者类,定义了观察者更新状态的抽象方法
4.ConcreteObserver类,具体的观察者类,根据构造方法得到通知者的状态改变,并进行跟新操作
1 /// <summary> 2 /// 具体观察者 3 /// </summary> 4 public class ConcreteObserver : Observer 5 { 6 /// <summary> 7 /// 观察者名字 8 /// </summary> 9 private readonly string _name; 10 11 /// <summary> 12 /// 观察者状态 13 /// </summary> 14 private string _observerState; 15 16 17 private ConcreteSubject _concreteSubject; 18 /// <summary> 19 /// 通知者对象 20 /// </summary> 21 public ConcreteSubject ConcreteSubject 22 { 23 get { return _concreteSubject; } 24 set { _concreteSubject = value; } 25 } 26 27 /// <summary> 28 /// 具体观察者构造方法 29 /// </summary> 30 /// <param name="subject">通知者对象</param> 31 /// <param name="name">观察者姓名</param> 32 public ConcreteObserver(ConcreteSubject subject, string name) 33 { 34 _concreteSubject = subject; 35 _name = name; 36 } 37 38 /// <summary> 39 /// 状态更新 40 /// </summary> 41 public override void Update() 42 { 43 _observerState = _concreteSubject.SubjectState; 44 Console.Write("观察者{0}的新状态是{1}", _name, _observerState); 45 } 46 }
5.客户端代码,实例化通知者,并且添加观察者,改变通知者状态,观察者一起更新
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 var subject = new ConcreteSubject(); 7 8 subject.Attach(new ConcreteObserver(subject, "X")); 9 10 subject.Attach(new ConcreteObserver(subject, "Y")); 11 12 subject.SubjectState = "123"; 13 14 subject.Notify(); 15 } 16 }
以上的观察者模式是实现了一个状态改变,观察者状态改变。但是有一个缺点,那就是一个通知,所有的观察者的状态都改变了,而且改变的都一样,这样在实际开发中是很少遇见的,我们不希望为了维持一致性而使各类紧密耦合,这样会给维护和复用都带来不便。
所以,下面我们需要对观察者模式进行改进,会用.NET中一个特殊方法,那就是事件委托。
委托——委托就是一种引用方法的类型。一旦为委托分配了方法,委托将于该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对方法的抽象,是函数的“类”,委托的实例将代表一个具体的方法。
委托可以搭载多个方法,所有方法将被依次唤醒,委托所搭载的方法并不需要属于同一个类,只要参数和返回值类型一致就行了。
1.ISubject接口,定义了通知者的通知方法和状态属性
1 /// <summary> 2 /// 通知者接口 3 /// </summary> 4 public interface ISubject 5 { 6 /// <summary> 7 /// 进行通知 8 /// </summary> 9 void Notify(); 10 11 /// <summary> 12 /// 通知者状态 13 /// </summary> 14 string SubjectState { get; set; } 15 }
2.Observer类,具体的观察者类,自身有不同的更新方法,但是参数和返回值一致,实现了什么可以不一致
1 /// <summary> 2 /// 股票行情观察者 3 /// </summary> 4 public class StockObserver 5 { 6 private readonly string _name; 7 private readonly ISubject _subject; 8 9 10 public StockObserver(string mame, ISubject subject) 11 { 12 _name = mame; 13 _subject = subject; 14 } 15 16 /// <summary> 17 /// 原Update方法 18 /// 先改成具体的差异化动作 19 /// </summary> 20 public void CloseStockMarket() 21 { 22 Console.WriteLine("{0}{1}关闭股票行情,继续工作", _subject.SubjectState, _name); 23 } 24 } 25 26 /// <summary> 27 /// NBA直播观察者 28 /// </summary> 29 public class NBAObserver 30 { 31 private readonly string _name; 32 private readonly ISubject _subject; 33 34 public NBAObserver(string mame, ISubject subject) 35 { 36 _name = mame; 37 _subject = subject; 38 } 39 40 /// <summary> 41 /// 原Update方法 42 /// 先改成具体的差异化动作 43 /// </summary> 44 public void CloseNBADirectSeeding() 45 { 46 Console.WriteLine("{0}{1}关闭NBA直播,继续工作", _subject.SubjectState, _name); 47 } 48 }
3.Subject类,具体的通知者,实现了接口的通知方法和状态属性,并且定义了一个委托,可以搭载各个观察者的更新需求
1 /// <summary> 2 /// 定义一个委托 3 /// 委托上的任务是给观察者通知 4 /// </summary> 5 public delegate void EventHandeler(); 6 7 /// <summary> 8 /// 通知者,实现通知接口 9 /// </summary> 10 public class Boss : ISubject 11 { 12 /// <summary> 13 /// 实例化委托 14 /// </summary> 15 public event EventHandeler Update; 16 17 /// <summary> 18 /// 调用委托方法去进行通知 19 /// 委托可以使委托链 20 /// 那么可以同时实现多种通知 21 /// </summary> 22 public void Notify() 23 { 24 Update(); 25 } 26 27 /// <summary> 28 /// 实现接口的通知状态属性 29 /// </summary> 30 public string SubjectState { get; set; } 31 } 32 33 public class Secretary : ISubject 34 { 35 public event EventHandeler Update; 36 37 public void Notify() 38 { 39 Update(); 40 } 41 42 string ISubject.SubjectState { get; set; } 43 }
4.客户端代码,创建通知者对象,并创建观察者对象,将观察者的更新需求搭载进通知者的委托链,然后改变通知者的状态,发出通知,观察者进行自我更新
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //创建通知者老板 6 var boss = new Boss(); 7 8 //创建具体的观察者同事 9 10 var colleague1 = new StockObserver("张三 ", boss); 11 12 var colleague2 = new NBAObserver("李四 ", boss); 13 14 //给通知者添加通知的事件 15 boss.Update+=new EventHandeler(colleague1.CloseStockMarket); 16 boss.Update+=new EventHandeler(colleague2.CloseNBADirectSeeding); 17 18 //老板回来了,状态更改 19 boss.SubjectState = "我胡汉三又回来啦,"; 20 //老板作为通知者通知观察者 21 boss.Notify(); 22 Console.ReadKey(); 23 24 } 25 }
以上内容部分参考程杰的《大话设计模式》一书