观察者模式 - Observer

本文深入探讨了观察者模式的原理、结构图及其在不同场景的应用,包括客户端代码示例和改进方案,如使用事件委托实现。文章还介绍了观察者模式的优缺点及实际应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

观察者模式(Observer),定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时会通知所有观察者对象,使它们能够自动更新自己。

观察者模式结构图:

Subject 类,可以翻译为主题或抽象通知者,一般用一个抽象类或一个接口实现。它把所有对观察者对象的引用保存在一个集合里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
abstract class Subject
{
 private IList<Observer> observers = new List<Observer>();

 //增加观察者
 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();
  }
 }
}

Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口,抽象观察者一般用一个抽象类或接口实现。更新接口通常包含一个Update()方法,这个方法叫做更新方法。
abstract class Observer
{
 public abstract void Update();
}

ConcreteSubject类,叫做具体主题或通知者,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
class ConcreteSubject : Subject
{
 private string subjectState;
 //具体被观察者状态
 public string SubjectState
 {
  get { return subjectState;}
  set { subjectState = value; }
 }
}

ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者通常用一个具体子类实现.

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;}
 }
}

---客户端代码---
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 ="ABS";
 s.Notify();

 Console.Read();
}

观察者模式一般用在,当一个对象的改变需要同时改变其它对象,补充一下,而且它不知道具体有多少对象有待改变时,应该考虑用该模式。
观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不影响另一边的变化。

观察者模式的不足之处,尽管已经用了依赖倒转原则,但是‘抽象通知者’还是依赖抽象观察者,也就是说,万一没有了抽象观察者这样的接口,我这通知就完成不了,另外就是每个具体观察者,它不一定是‘更新’的方法要调用呀。如果通知者和观察者之间根本就互相不知道,由客户端来决定通知谁就好了

改进,用事件委托实现
看股票观察者类和看NBA观察者类去掉了父类抽象观察者类
//看股票的同事
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;
 }

 //关闭股票行情 方法‘更新’名改为这个了
 public void CloseNBADirectSeeding()
 {
  Console.WriteLine("{0} {1} 关闭NBA直播继续工作",sub.SubjectState,name);
 }
}

现象中就是这样的,方法名本就不一定相同
抽象通知者,由于不希望依赖抽象观察者,所以增加,减少的方法也就没有必要了(抽象观察者已经不存在了)
//通知者接口
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 Notidy()
 {
  Update();
 }

 public string SubjectState
 {
  get { return action;}
  set { action = value;}
 }
}

---客户端代码---
static Main(string[] args)
{
 Secretary mm = new Secretary();
 
 StockObserver tongshi1 = new StockObserver("李同事",mm);
 NBAObserver tongshi2 = new NBAObserver("姚同事",mm);

 mm.Update += new EventHandler(tongshi1.CloseStockMarket);
 mm.Update += new EventHandler(tongshi2.CloseNABDirectSeeding);

 mm.SubjectState = "Boss 回来了";
 mm.Notify();

 Console.Read();
}

委托就是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为,委托方法的使用可以像其它任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的‘类’,委托的实例将代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被依次唤起,更重要的是,它可以使得委托对象所搭载的方法并不需要同一个类。但委托也是有前提的,那就是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值