设计模式-行为型-观察者模式
观察者模式(Observer):
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式的角色:
1)抽象目标(Subject):也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
2)具体目标(ConcreteSubject):也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
3)抽象观察者(Observer):它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
4)具体观察者(ConcreteObserver):实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 Subject subject = new ConcreteSubject(); 6 Observer observer1 = new ConcreteObserver1(); 7 Observer observer2 = new ConcreteObserver2(); 8 subject.Attach(observer1); 9 subject.Attach(observer2); 10 subject.Notify(); 11 } 12 } 13 14 /// <summary> 15 /// 抽象目标 16 /// </summary> 17 internal abstract class Subject 18 { 19 /// <summary> 20 /// 新增观察者 21 /// </summary> 22 /// <param name="o"></param> 23 public abstract void Attach(Observer o); 24 25 /// <summary> 26 /// 删除观察者 27 /// </summary> 28 /// <param name="o"></param> 29 public abstract void Detach(Observer o); 30 31 /// <summary> 32 /// 通知观察者 33 /// </summary> 34 public abstract void Notify(); 35 } 36 37 /// <summary> 38 /// 抽象观察者 39 /// </summary> 40 internal abstract class Observer 41 { 42 public abstract void update(); 43 } 44 45 /// <summary> 46 /// 具体目标 47 /// </summary> 48 internal class ConcreteSubject : Subject 49 { 50 private System.Collections.Generic.List<Observer> observers 51 = new System.Collections.Generic.List<Observer>(); 52 53 public override void Attach(Observer o) 54 { 55 if (o != null) 56 observers.Add(o); 57 } 58 59 public override void Detach(Observer o) 60 { 61 if (o != null) 62 observers.Remove(o); 63 } 64 65 public override void Notify() 66 { 67 Console.WriteLine("目标发生变化"); 68 foreach (var observer in observers) 69 { 70 observer.update(); 71 } 72 } 73 } 74 75 /// <summary> 76 /// 具体观察者1 77 /// </summary> 78 internal class ConcreteObserver1 : Observer 79 { 80 public override void update() 81 { 82 Console.WriteLine("观察者1更新"); 83 } 84 } 85 86 /// <summary> 87 /// 具体观察者2 88 /// </summary> 89 internal class ConcreteObserver2 : Observer 90 { 91 public override void update() 92 { 93 Console.WriteLine("观察者2更新"); 94 } 95 }
事例:
学校的“铃”是事件源和目标,"学生"是事件监听器和具体观察者,"铃声"是事件类。当下课时间到,会触发铃发声,这时会生成“铃声”事件;学生就会做一些事情。。。
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 创建下课铃声 6 Bell subject = new FinishClassBell(); 7 // 创建观察者 8 Observer xiaoming = new XiaoMing(); 9 Observer xiaohong = new XiaoHong(); 10 Observer xiaowang = new XiaoWang(); 11 // 观察者随着听着下课铃 12 subject.Attach(xiaoming); 13 subject.Attach(xiaohong); 14 subject.Attach(xiaowang); 15 // 下课铃响了,通知大家 16 subject.Notify(); 17 } 18 } 19 20 /// <summary> 21 /// 铃声:抽象目标 22 /// </summary> 23 internal abstract class Bell 24 { 25 /// <summary> 26 /// 新增观察者 27 /// </summary> 28 /// <param name="o"></param> 29 public abstract void Attach(Observer o); 30 31 /// <summary> 32 /// 删除观察者 33 /// </summary> 34 /// <param name="o"></param> 35 public abstract void Detach(Observer o); 36 37 /// <summary> 38 /// 通知观察者 39 /// </summary> 40 public abstract void Notify(); 41 } 42 43 /// <summary> 44 /// 同学下课做事情:抽象观察者 45 /// </summary> 46 internal abstract class Observer 47 { 48 public abstract void DoSomeThings(); 49 } 50 51 /// <summary> 52 /// 下课铃声:具体目标 53 /// </summary> 54 internal class FinishClassBell : Bell 55 { 56 private System.Collections.Generic.List<Observer> observers 57 = new System.Collections.Generic.List<Observer>(); 58 59 public override void Attach(Observer o) 60 { 61 if (o != null) 62 observers.Add(o); 63 } 64 65 public override void Detach(Observer o) 66 { 67 if (o != null) 68 observers.Remove(o); 69 } 70 71 public override void Notify() 72 { 73 Console.WriteLine("铃声响了..."); 74 foreach (var observer in observers) 75 { 76 observer.DoSomeThings(); 77 } 78 } 79 } 80 81 /// <summary> 82 /// 小明同学 83 /// </summary> 84 internal class XiaoMing : Observer 85 { 86 public override void DoSomeThings() 87 { 88 Console.WriteLine("小明同学:下课打篮球"); 89 } 90 } 91 92 /// <summary> 93 /// 小红同学 94 /// </summary> 95 internal class XiaoHong : Observer 96 { 97 public override void DoSomeThings() 98 { 99 Console.WriteLine("小红同学:呜呜呜,还有一节课"); 100 } 101 } 102 103 /// <summary> 104 /// 小王同学 105 /// </summary> 106 internal class XiaoWang : Observer 107 { 108 public override void DoSomeThings() 109 { 110 Console.WriteLine("小王同学:饿死了,开吃..."); 111 } 112 }
观察者模式的应用场景:
1)对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
2)当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
观察者模式的优缺点:
优点:观察者和被观察者是抽象耦合的;建立的一套触发机制。
缺点:
1)如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2)如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
.NET中观察者模式的应用:
1 // 委托充当订阅者接口类 2 public delegate void NotifyEventHandler(object sender); 3 4 // 抽象订阅号类 5 public class TenXun 6 { 7 public NotifyEventHandler NotifyEvent; 8 9 public string Symbol { get; set; } 10 public string Info { get; set; } 11 public TenXun(string symbol, string info) 12 { 13 this.Symbol = symbol; 14 this.Info = info; 15 } 16 17 // 新增对订阅号列表的维护操作 18 public void AddObserver(NotifyEventHandler ob) 19 { 20 NotifyEvent += ob; 21 } 22 // 删除对订阅号列表的维护操作 23 public void RemoveObserver(NotifyEventHandler ob) 24 { 25 NotifyEvent -= ob; 26 } 27 // 推送消息 28 public void Update() 29 { 30 if (NotifyEvent != null) 31 { 32 NotifyEvent(this); 33 } 34 } 35 } 36 37 // 具体订阅号类 38 public class TenXunGame : TenXun 39 { 40 public TenXunGame(string symbol, string info) 41 : base(symbol, info) 42 { 43 } 44 } 45 46 // 具体订阅者类 47 public class Subscriber 48 { 49 public string Name { get; set; } 50 public Subscriber(string name) 51 { 52 this.Name = name; 53 } 54 55 public void ReceiveAndPrint(Object obj) 56 { 57 TenXun tenxun = obj as TenXun; 58 59 if (tenxun != null) 60 { 61 Console.WriteLine("Notified {0} of {1}'s" + " Info is: {2}", Name, tenxun.Symbol, tenxun.Info); 62 } 63 } 64 } 65 66 static void Main(string[] args) 67 { 68 TenXun tenXun = new TenXunGame("TenXun Game", "Have a new game published ...."); 69 Subscriber lh = new Subscriber("Learning Hard"); 70 Subscriber tom = new Subscriber("Tom"); 71 72 // 添加订阅者 73 tenXun.AddObserver(new NotifyEventHandler(lh.ReceiveAndPrint)); 74 tenXun.AddObserver(new NotifyEventHandler(tom.ReceiveAndPrint)); 75 tenXun.Update(); 76 77 Console.WriteLine("-----------------------------------"); 78 Console.WriteLine("移除Tom订阅者"); 79 tenXun.RemoveObserver(new NotifyEventHandler(tom.ReceiveAndPrint)); 80 tenXun.Update();81 }
从上面代码可以看出,使用事件和委托实现的观察者模式中,减少了订阅者接口类的定义,此时,.NET中的委托正式充到订阅者接口类的角色。使用委托和事件,确实简化了观察者模式的实现,减少了一个IObserver接口的定义。