设计模式(十)观察者模式
观察者模式(Observer),又叫发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。或者说,当一个抽象模型由两个方面,其中一方面依赖于另一方面,此时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不依赖于具体。从而使得各自的变化都不会影响另一边的变化。

基本代码
1 // 抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫更新接口。 2 // 抽象观察者一般用一个抽象类或者一个接口实现。 3 abstract class Observer 4 { 5 // 更新方法 6 public abstract void Update(); 7 } 8 9 // Subject 类,可翻译为主题或抽象通知者,一般用一个抽象者或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集, 10 // 每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。 11 abstract class Subject 12 { 13 private IList<Observer> observers = new List<Observer>(); 14 15 // 增加观察者 16 public void Attach(Observer observer) 17 { 18 observer.Add(observer); 19 } 20 // 移除观察者 21 public void Detach(Observer observer) 22 { 23 observer.Remove(observer); 24 } 25 // 通知 26 public void Notify() 27 { 28 foreach(Observer o in observers) 29 { 30 o.Update(); 31 } 32 } 33 } 34 35 // 具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。 36 class ConcreteSubject : Subject 37 { 38 private string subjectState; 39 // 具体被观察者状态 40 public string SubjectState 41 { 42 get { return subjectState; } 43 set { subjectState = value; } 44 } 45 } 46 47 // 具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。 48 // 具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现 49 class ConcreteObserver : Observer 50 { 51 private string name; 52 private string observerState; 53 private ConcreteSubject subject; 54 55 public ConcreteObserver(ConcreteSubject subject, string name) 56 { 57 this.subject = subject; 58 this.name = name; 59 } 60 61 public override void Update() 62 { 63 observerState = subject.SubjectState; 64 Console.WriteLine("观察者{0}的新状态是{1}", name, observerState); 65 } 66 67 public ConcreteSubject Subject 68 { 69 get { return subject; } 70 set { subject = value; } 71 } 72 } 73 74 // 客户端 75 static void Main(string[] args) 76 { 77 ConcreteSubject s = new ConcreteSubject(); 78 79 s.Attach(new ConcreteObserver(s, "X")); 80 s.Attach(new ConcreteObserver(s, "Y")); 81 s.Attach(new ConcreteObserver(s, "Z")); 82 83 s.SubjectState = "ABC"; 84 s.Notify(); 85 86 Console.Read(); 87 }
【例】
事件委托
委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的 “类”,委托的实例将代表一个具体的函数。
委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。
delegate void EventHandler(); 可以理解为声明了一个特殊的 “类”;
public event EventHandler Update; 可以理解为声明了一个 “类” 的变量。
new EventHandler(tongshi1.CloseStockMarket); 一个委托的实例,代表一个具体的函数。
一旦为委托分配了方法,委托将与该方法具有完全相同的行为。而且,一个委托可以搭载多个方法,所有方法被依次唤起。更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类。

基本代码
1 // 看股票的同事 2 class StockObserver 3 { 4 private string name; 5 private Subject sub; 6 7 public StockObserver(string name, Subject sub) 8 { 9 this.name = name; 10 this.sub = sub; 11 } 12 13 // 关闭股票行情 14 public void CloseStockMarket() 15 { 16 Console.WriteLine(" "); 17 } 18 } 19 20 // 看 NBA 的同事 21 class NBAObserver 22 { 23 private string name; 24 private Subject sub; 25 26 public NBAObserver(string name, Subject sub) 27 { 28 this.name = name; 29 this.sub = sub; 30 } 31 32 // 关闭 NBA 直播 33 public void CloseNBADirectSeeding() 34 { 35 Console.WriteLine(" "); 36 } 37 } 38 39 // 通知者接口 40 interface Subject 41 { 42 void Notify(); 43 44 string SubjectState 45 { 46 get {}; 47 set {}; 48 } 49 } 50 51 // 声明一个委托,事件处理程序,无参数,无返回值 52 delegate void EventHandler(); 53 54 // 老板类 55 class Boss : Subject 56 { 57 // 声明一事件 Update,类型为委托 EventHandler 58 public event EventHandler Update; 59 private string action; 60 61 public void Notify() 62 { 63 Update(); 64 } 65 66 public string SubjectState 67 { 68 get { return action; } 69 set { action = value; } 70 } 71 } 72 73 // 前台秘书类 74 class Secretary : Subject 75 { 76 } 77 78 // 客户端 79 Boss boss = new Boss(); 80 81 // 看股票的同事 82 StockObserver tongshi1 = new StockObserver("魏", boss); 83 84 // 看 NBA 的同事 85 NBAObserver tongshi2 = new NBAObserver("易", boss); 86 87 boss.Update += new EventHandler(tongshi1.CloseStockMarket); 88 boss.Update += new EventHandler(tongshi2.CloseNBADirectSeeding); 89 90 // 91 boss.SubjectState = "work"; 92 93 // 发出通知 94 boss.Notify();

浙公网安备 33010602011771号