观察者(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运行。
}