最近加班越来越多
,阿三感到非常疲惫,于是上班的时候想摸摸鱼
。于是趁老板出去的时候,打开手机,看看股票是否跌破3000点了,看看基金有没有扳回来一点。但是万一老板回来了怎么办?还好他人脉广,认识前台的大头,跟大头打个招呼,老板一回来,就发个信息给他
。
这就是典型的观察者模式,又叫发布-订阅(Publish/Subscribe)模式
。观其其他事务的状态,根据其状态改变做出对应的策略。
::: block-1
观察者模式
:定义了一种一对多的依赖关系
,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象
,使它们能够自动更新自己
。
:::
观察者与通知者均使用抽象类
,方便通知者和观察者的扩展与个例的实现,观察不同对象,通知者也有不同的通知形式。
可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现,可以添加或者移除观察者,并通知
。
abstract class Subject
{
private IList observers = new List();
//增加观察者
public void Attach(Observer observer)
{
observers.Add(observer);
}
//移除观察者
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}
具体的通知者,将有关状态存入具体的观察者对象
,在具体通知者内部状态改变时,给所有登记过的观察者发出通知,就是通知者发现老板来了,要赶紧通知正在摸鱼的同事们。
//具体通知者
class ConcreteSubject : Subject
{
private string subjectState;
//具体通知者状态
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
}
抽象观察者,在得到通知者的通知时,更新自己,这个接口叫更新接口
,通常包含一个Update
方法,这个方法叫更新方法
abstract class Observer
{
public abstract void Update();
}
具体观察者。具体观察者角色可以保存一个指向具体通知者对象的引用
。
class ConcreteObserver : Observer
{
private string name;
private string observerState;
private ConcreteSubject subject;
public ConcreteObserver(ConcreteSubject subject, string name)
{
this.subject = subject;
this.name = name;
}
//更新
public override void Update()
{
observerState = subject.SubjectState;
Console.WriteLine("观察者{0}的新状态是{1}",
name, observerState);
}
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
}
}
class Program
{
static void Main(string[] args)
{
ConcreteSubject s = new ConcreteSubject();
s.Attach(new ConcreteObserver(s, "X"));
s.Attach(new ConcreteObserver(s, "Y"));
s.Attach(new ConcreteObserver(s, "Z"));
s.SubjectState = "ABC";
s.Notify();
Console.Read();
}
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uB4Wlz0a-1658836565459)(https://files.mdnice.com/user/27157/3226af16-1c39-44b0-b5de-2faf87d132c3.png)]
不知道具体有多少对象
需要改变;一方面依赖于另一方面
,可以将两者封装在独立的对象中
,使他们各自独立地改变和复用
;解除耦合
,耦合的双方都依赖于抽象而非具体。独立的观察者类都需要实现一个Observer的接口
,而有些独立的类之间并没有观察者接口,如果没有,通知就没法下发了。
应该由客户端决定,谁改变了通知谁。
::: block-1
委托就是一种引用方法的类型。
一旦为委托分配了方法,
委托将与该方法具有完全相同的行为
。委托可以有参数和返回值,可以看成是
对函数的抽象
,是函数的“类”,委托的实例代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被依次唤起。方法的入参和返回值要与委托的定义相同
。
:::
看具体代码:
using System;
using System.Collections.Generic;
using System.Text;
namespace 观察者模式
{
class Program
{
static void Main(string[] args)
{
//老板Randy
Boss huhansan = new Boss();
//看股票的同事
StockObserver tongshi1 = new StockObserver("阿三", huhansan);
//看NBA的同事
NBAObserver tongshi2 = new NBAObserver("大头", huhansan);
/// 将不同的方法搭载到老板的更新上,从而通知观察者做各自的更新,
// 观察者不需要统一的Observer接口
huhansan.Update += new EventHandler(tongshi1.CloseStockMarket);
huhansan.Update += new EventHandler(tongshi2.CloseNBADirectSeeding);
//老板回来
huhansan.SubjectState = "我Randy回来了!";
//发出通知
huhansan.Notify();
Console.Read();
}
}
//通知者接口
interface Subject
{
void Notify();
string SubjectState
{
get;
set;
}
}
//事件处理程序的委托
delegate void EventHandler();
class Secretary : Subject
{
//声明一事件Update,类型为委托EventHandler
public event EventHandler Update;
private string action;
public void Notify()
{
Update();
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
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; }
}
}
//看股票的同事
class StockObserver
{
private string name;
private Subject sub;
public StockObserver(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
//关闭股票行情
public void CloseStockMarket()
{
Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SubjectState, name);
}
}
//看NBA的同事
class NBAObserver
{
private string name;
private Subject sub;
public NBAObserver(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
//关闭NBA直播
public void CloseNBADirectSeeding()
{
Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SubjectState, name);
}
}
}
运行结果: