观察者(Publish/Subscribe)模式定义: 又叫发布—订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
优点:
解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响另一边变化,符合依赖倒转原则。
缺点:
抽象通知者还是依赖抽象观察者,不能进一步的解除耦合,改进方式是使用委托。
使用场景:当一个对象的改变需要同时改变其他对象的时候,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
代码背景:公司的两个员工时刻监听老板是否回来了,因为他俩想要趁老板不在做自己的事情,如果有人通知他们老板回来了,他们就会放下手中的事情继续工作。很不巧这次是老板自己通知的这两个员工——他来了。
通知者:老板。
观察者:两个员工,根据通知的内容作出不一样的行为。
抽象通知者:把所有对观察者对象的引用(Attach、Detach、Notify)保存在一个聚集里,每个主题都可以有任何数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象,并且不需要知道具体观察者是谁,任何一个具体观察者也不需要知道其他观察者的存在。
interface Subject //通知者接口 { void Attach(Observer observer); void Detach(Observer observer); void Notify(); string SubjectState //通知者状态 { get; set; } }
抽象观察者 :观察者本来可以是任何一个对象,使用抽象增加代码可扩展性(或者用接口)
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(); }
Boss类:具体主题
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); } //通知(包含了员工的Update方法,一旦被通知,员工就执行Update方法) public void Notify () { foreach (Observer o in observers)//遍历所有要通知的对象,然他们依次执行Update方法 o.Update(); } //老板状态(重写接口) public string SubjectState { get {return action;} set {action = value;} } }
员工类:具体观察者
class StockObserver:Observer ///看股票的员工 { public StockObserver(string name, Subject sub) : base(name, sub)//抽象观察者,观察者本来就可以是任何人 { } public override void Update() //重写更新方法 { Console.WriteLine("{0}{1}关闭股票行情,继续工作!", sub.SubjectState , name); } } class NBAobserver : Observer //看NBA的员工 { public NBAobserver(string name, Subject sub) : base(name, sub) { } public override void Update() //重写更新方法 { Console.WriteLine("{0}{1}关闭NBA直播,继续工作!", sub.SubjectState, name); } }
客户端:
static void Main(string[] args) { //老板 Boss bazong = new Boss(); //摸鱼的同事 StockObserver tongshi1 = new StockObserver("江小鱼",bazong); NBAobserver tongshi2 = new NBAobserver ("李建刚", bazong); //老板默默记下要通知的两位同事 bazong.Attach(tongshi1); bazong.Attach(tongshi2); //老板大意了,江小鱼没听见,没被通知到,所以减去这个人 bazong.Detach(tongshi1); //老板状态改变 bazong.SubjectState = "我霸道总裁回来了!"; //发出通知,员工执行Update方法 bazong.Notify(); Console.Read(); } }
例子中没有关于数据和状态的变化通知,只是简单通知到各个观察者,告诉他们被观察者有行动。
观察者模式在关于目标角色、观察者角色通信的具体实现中,有两个版本。
一种情况便是目标角色在发生变化后,仅仅告诉观察者角色“我变化了”,观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到。这种模式被很形象的称为:拉模式——就是说变化的信息是观察者角色主动从目标角色中“拉”出来的。
还有一种方法,那就是我目标角色“服务一条龙”,通知你发生变化的同时,通过一个参数将变化的细节传递到观察者角色中去。这就是“推模式”——管你要不要,先给你啦。
这两种模式的使用,取决于系统设计时的需要。如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适。如果目标角色比较简单,则“拉模式”就很合适。
委托(delegate)定义:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
委托是一种引用方法的类型,一旦被委托分配了方法,委托将与该方法具有相同的行为。
//Update是事件,EventHandler是委托类型,tongshi1.CloseGameMarket是被委托的方法 //表示将tongshi1的CloseGameMarket方法通过实例化委托EventHandler登记到laoban的事件Update当中。 //+=表示add_CatShout,增加委托实例对象 laoban.Update += new EventHandler(tongshi1.CloseGameMarket );
委托是对函数的封装,可以给方法的特征制定一个名称(如下例的“EventHandler”)。
而事件是委托的一种特殊形式,当发生事件时(如下例的“老板来视察”),委托类型(事件处理对象,也就是下例的“EventHandler”)通知过程(如下例的tongshi1.CloseGameMarket、tongshi2.CloseZbObserver)。
玩游戏的同事:
class GameObserver { private string name; private Subject sub; public GameObserver(string name,Subject sub) { this.name = name; this.sub = sub; } //关闭游戏 public void CloseGameMarket() { Console.WriteLine("{0}{1}关闭王者荣耀,继续工作!",sub.SubjectState,name); } }
看直播的同事
class ZbObserver { private string name; private Subject sub; public ZbObserver (string name,Subject sub) { this.name = name; this.sub = sub; } //关闭直播 public void CloseZbObserver() { Console.WriteLine("{0}{1}关闭直播,继续工作!",sub.SubjectState,name); } }
通知者状态接口:
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; } } }
客户端:
static void Main(string[] args) { Boss laoban = new Boss(); //玩游戏的同事 GameObserver tongshi1 = new GameObserver("秦霜",laoban); //看直播的同事 ZbObserver tongshi2 = new ZbObserver("楚楚",laoban); //实例化一个委托,委托的实例是tongshi1的CloseGameMarket laoban.Update += new EventHandler(tongshi1.CloseGameMarket ); laoban.Update += new EventHandler(tongshi2.CloseZbObserver ); //老板回来 laoban.SubjectState = "老板:我来视察工作了\n"; //发出通知 laoban.Notify();//Notify()方法一运行,里面的Update事件就运行,Notify()方法就委托EventHandler去通知方法tongshi1.CloseGameMarket运行。 }