C# 设计模式系列教程-观察者模式

1. 概述

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

2. 解决的问题

  将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。

3. 模式中的角色

  3.1 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

  3.2 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

  3.3 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

  3.4 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

4. 模式解读

  4.1 观察者模式的类图  

  4.2 观察者模式的代码

 /// <summary>
 /// 抽象主题类
 /// </summary>
 public abstract class Subject
 {
 private IList<Observer> observers = new List<Observer>();

 /// <summary>
 /// 增加观察者
 /// </summary>
 /// <param name="observer"></param>
 public void Attach(Observer observer)
 {
  observers.Add(observer);
 }

 /// <summary>
 /// 移除观察者
 /// </summary>
 /// <param name="observer"></param>
 public void Detach(Observer observer)
 {
  observers.Remove(observer);
 }

 /// <summary>
 /// 向观察者(们)发出通知
 /// </summary>
 public void Notify()
 {
  foreach (Observer o in observers)
  {
  o.Update();
  }
 }
 }

 /// <summary>
 /// 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
 /// </summary>
 public abstract class Observer
 {
 public abstract void Update();
 }

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

 /// <summary>
 /// 具体观察者的状态
 /// </summary>
 public string SubjectState
 {
  get { return subjectState; }
  set { subjectState = value; }
 }
 }

 /// <summary>
 /// 具体观察者,实现抽象观察者角色所要求的更新接口,已是本身状态与主题状态相协调
 /// </summary>
 public class ConcreteObserver : Observer
 {
 private string observerState;
 private string name;
 private ConcreteSubject subject;

 /// <summary>
 /// 具体观察者用一个具体主题来实现
 /// </summary>
 public ConcreteSubject Subject
 {
  get { return subject; }
  set { subject = value; }
 }

 public ConcreteObserver(ConcreteSubject subject, string name)
 {
  this.subject = subject;
  this.name = name;
 }

 /// <summary>
 /// 实现抽象观察者中的更新操作
 /// </summary>
 public override void Update()
 {
  observerState = subject.SubjectState;
  Console.WriteLine("The observer's state of {0} is {1}", name, observerState);
 }
 }

  4.3 客户端代码

 class Program
 {
 static void Main(string[] args)
 {
  // 具体主题角色通常用具体自来来实现
  ConcreteSubject subject = new ConcreteSubject();

  subject.Attach(new ConcreteObserver(subject, "Observer A"));
  subject.Attach(new ConcreteObserver(subject, "Observer B"));
  subject.Attach(new ConcreteObserver(subject, "Observer C"));

  subject.SubjectState = "Ready";
  subject.Notify();

  Console.Read();
 }
 }

  运行结果

5. 模式总结

  5.1 优点

    5.1.1 观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。

  5.2 缺点

    5.2.1 依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。

  5.3 适用场景

    5.3.1 当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。

    5.3.2 一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。

6. 模式引申,应用C#中的事件委托来彻底解除通知者和观察者之间的耦合。

   6.1 关于委托的定义:委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法有相同的行为。委托方法可以像其它任何方法一样,具有参数和返回值。委托可以看作是对函数(方法)的的抽象,是函数的“类”,委托的实例代表一个(或多个)具体的函数,它可以是多播的。

   6.2 关于事件:事件基于委托,为委托提供了一种发布/订阅机制。事件的订阅与取消与我们刚才讲的观察者模式中的订阅与取消类似,只是表现形式有所不同。在观察者模式中,订阅使用方法Attach()来进行;在事件的订阅中使用“+=”。类似地,取消订阅在观察者模式中用Dettach(),而事件的取消用“-=”。

7. 下面例子分别用观察者模式,事件机制来实现

  7.1 实例描述:客户支付了订单款项,这时财务需要开具发票,出纳需要记账,配送员需要配货。

  7.2 观察者模式的实现

    7.2.1 类图

    7.2.2 代码实现

 /// <summary>
 /// 抽象观察者
 /// </summary>
 public interface ISubject
 {
 void Notify();
 }

 /// <summary>
 /// 工作岗位,作为这里的观察者的抽象
 /// </summary>
 public abstract class JobStation
 {
 public abstract void Update();
 }

 /// <summary>
 /// 具体主题,这里是客户
 /// </summary>
 public class Customer : ISubject
 {
 private string customerState;

 private IList<JobStation> observers = new List<JobStation>();

 /// <summary>
 /// 增加观察者
 /// </summary>
 /// <param name="observer"></param>
 public void Attach(JobStation observer)
 {
  this.observers.Add(observer);
 }

 /// <summary>
 /// 移除观察者
 /// </summary>
 /// <param name="observer"></param>
 public void Detach(JobStation observer)
 {
  this.observers.Remove(observer);
 }

 /// <summary>
 /// 客户状态
 /// </summary>
 public string CustomerState
 {
  get { return customerState; }
  set { customerState = value; }
 }

 public void Notify()
 {
  foreach (JobStation o in observers)
  {
  o.Update();
  }
 }
 }

 /// <summary>
 /// 会计
 /// </summary>
 public class Accountant : JobStation
 {
 private string accountantState;
 private Customer customer;

 public Accountant(Customer customer)
 {
  this.customer = customer;
 }

 /// <summary>
 /// 更新状态
 /// </summary>
 public override void Update()
 {
  if (customer.CustomerState == "已付款")
  {
  Console.WriteLine("我是会计,我来开具发票。");
  accountantState = "已开发票";
  }
 }
 }

 /// <summary>
 /// 出纳
 /// </summary>
 public class Cashier : JobStation
 {
 private string cashierState;
 private Customer customer;

 public Cashier(Customer customer)
 {
  this.customer = customer;
 }

 public override void Update()
 {
  if (customer.CustomerState == "已付款")
  {
  Console.WriteLine("我是出纳员,我给登记入账。");
  cashierState = "已入账";
  }
 }
 }

 /// <summary>
 /// 配送员
 /// </summary>
 public class Dilliveryman : JobStation
 {
 private string dillivierymanState;
 private Customer customer;

 public Dilliveryman(Customer customer)
 {
  this.customer = customer;
 }

 public override void Update()
 {
  if (customer.CustomerState == "已付款")
  {
  Console.WriteLine("我是配送员,我来发货。");
  dillivierymanState = "已发货";
  }
 }
 }

    7.2.3 客户端代码

 class Program
 {
 static void Main(string[] args)
 {

  Customer subject = new Customer();

  subject.Attach(new Accountant(subject));
  subject.Attach(new Cashier(subject));
  subject.Attach(new Dilliveryman(subject));

  subject.CustomerState = "已付款";
  subject.Notify();

  Console.Read();
 }
 }

    运行结果:

    我是会计,我来开具发票。
    我是出纳员,我给登记入账。
    我是配送员,我来发货。

  7.3 事件实现

    7.3.1 类图

    通过类图来看,观察者和主题之间已经不存在任何依赖关系了。

    7.3.2 代码实现

 /// <summary>
 /// 抽象主题
 /// </summary>
 public interface ISubject
 {
 void Notify();
 }

 /// <summary>
 /// 声明委托
 /// </summary>
 public delegate void CustomerEventHandler();

 /// <summary>
 /// 具体主题
 /// </summary>
 public class Customer : ISubject
 {
 private string customerState;

 // 声明一个委托事件,类型为 CustomerEventHandler
 public event CustomerEventHandler Update;

 public void Notify()
 {
  if (Update != null)
  {
  // 使用事件来通知给订阅者
  Update();
  }
 }

 public string CustomerState
 {
  get { return customerState; }
  set { customerState = value; }
 }
 }

 /// <summary>
 /// 财务,已经不需要实现抽象的观察者类,并且不用引用具体的主题
 /// </summary>
 public class Accountant
 {
 private string accountantState;

 public Accountant()
 { }

 /// <summary>
 /// 开发票
 /// </summary>
 public void GiveInvoice()
 {
  Console.WriteLine("我是会计,我来开具发票。");
  accountantState = "已开发票";
 }
 }

 /// <summary>
 /// 出纳,已经不需要实现抽象的观察者类,并且不用引用具体的主题
 /// </summary>
 public class Cashier
 {
 private string cashierState;

 public void Recoded()
 {
  Console.WriteLine("我是出纳员,我给登记入账。");
  cashierState = "已入账";
 }
 }

 /// <summary>
 /// 配送员,已经不需要实现抽象的观察者类,并且不用引用具体的主题
 /// </summary>
 public class Dilliveryman
 {
 private string dillivierymanState;

 public void Dilliver()
 {
  Console.WriteLine("我是配送员,我来发货。");
  dillivierymanState = "已发货";
 }
 }

    7.3.3 客户端代码

 class Program
 {
 static void Main(string[] args)
 {

  Customer subject = new Customer();

  Accountant accountant = new Accountant();
  Cashier cashier = new Cashier();
  Dilliveryman dilliveryman = new Dilliveryman();

  // 注册事件
  subject.Update += accountant.GiveInvoice;
  subject.Update += cashier.Recoded;
  subject.Update += dilliveryman.Dilliver;

  /*
  * 以上写法也可以用下面代码来替换
  subject.Update += new CustomerEventHandler(accountant.GiveInvoice);
  subject.Update += new CustomerEventHandler(cashier.Recoded);
  subject.Update += new CustomerEventHandler(dilliveryman.Dilliver);
  */

  subject.CustomerState = "已付款";
  subject.Notify();

  Console.Read();
 }
 }

    运行结果

    我是会计,我来开具发票。
    我是出纳员,我给登记入账。
    我是配送员,我来发货。

(0)

相关推荐

  • C#设计模式之单例模式实例讲解

    前言 最近开始花点心思研究下设计模式,主要还是让自己写的代码可重用性高.保证代码可靠性.所谓设计模式,我找了下定义:是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.毫无疑问,设计模式于己于他人于系统都是多赢的:设计模式使代码编制真正工程化:设计模式是软件工程的基石脉络,如同大厦的结构一样. 为什么要提倡"Design Pattern(设计模式)"? 根本原因是为了代码复用,增加可维护性.因此这次我们来学习下设计模式,最后会通过C#语言来实现这些设计模式作为例子,深刻

  • C# 观察者模式实例介绍

    观察者模式 观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主体对象,这个主题对象在状态发生变化时,会通知所有观察者.当一个对象改变需要同时改变其他对象,而且他不知道具体有多少对象需要改变的时候,应该考虑使用观察者模式. 观察者结构图: 使用场景:老板回来通知员工需要进入工作状态. 定义观察者的抽象类: 复制代码 代码如下: abstract class Observer { protected string name; protected ISubject sub; pu

  • 实例解析C#设计模式编程中简单工厂模式的使用

    简单工厂模式的介绍 说到简单工厂,自然的第一个疑问当然就是什么是简单工厂模式了? 在现实生活中工厂是负责生产产品的,同样在设计模式中,简单工厂模式我们也可以理解为负责生产对象的一个类, 我们平常编程中,当使用"new"关键字创建一个对象时,此时该类就依赖与这个对象,也就是他们之间的耦合度高,当需求变化时,我们就不得不去修改此类的源码,此时我们可以运用面向对象(OO)的很重要的原则去解决这一的问题,该原则就是--封装改变,既然要封装改变,自然也就要找到改变的代码,然后把改变的代码用类来封

  • c#设计模式 适配器模式详细介绍

    后续内容将包括以下结构模式: 适配器模式(Adapter):Match interfaces of different classes合成模式(Composite):A tree structure of simple and composite objects装饰模式(Decorator):Add responsibilities to objects dynamically代理模式(Proxy):An object representing another object享元模式(Flywei

  • C#设计模式之观察者模式实例讲解

    前言 最近开始花点心思研究下设计模式,主要还是让自己写的代码可重用性高.保证代码可靠性.所谓设计模式,我找了下定义:是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.毫无疑问,设计模式于己于他人于系统都是多赢的:设计模式使代码编制真正工程化:设计模式是软件工程的基石脉络,如同大厦的结构一样. 为什么要提倡"Design Pattern(设计模式)"? 根本原因是为了代码复用,增加可维护性.因此这次我们来学习下设计模式,最后会通过C#语言来实现这些设计模式作为例子,深刻

  • C#中观察者模式的3种实现方式

    说起观察者模式,估计在园子里能搜出一堆来.所以写这篇博客的目的有两点: 1.观察者模式是写松耦合代码的必备模式,重要性不言而喻,抛开代码层面,许多组件都采用了Publish-Subscribe模式,所以我想按照自己的理解重新设计一个使用场景并把观察者模式灵活使用在其中 2.我想把C#中实现观察者模式的三个方案做一个总结,目前还没看到这样的总结 现在我们来假设这样的一个场景,并利用观察者模式实现需求: 未来智能家居进入了每家每户,每个家居都留有API供客户进行自定义整合,所以第一个智能闹钟(sma

  • C#中利用代理实现观察者设计模式详解

    界面开发中,经常使用观察者设计模式来实现文档/视图模式,当文档内容改变时,作为观察者的用户视图必须相应作出调整以向用户呈现文档的状态.由于语言机制的不同,观察者设计模式在不同的语言中实现方法也不尽相同. 在MFC的文档/视图模式中,每当文档内容改变都需要调用UpdateAllView函数来更新视图,该函数会遍历文档的每一个视图,调用每个视图的更新函数来更新视图,为此文档须登记每一个使用该文档的视图.C#中观察者设计模式的实现也可以采用这种方法,但C#提供的代理(delegate)机制为实现观察者

  • C# 设计模式系列教程-策略模式

    在讲策略模式之前,我先给大家举个日常生活中的例子,从首都国际机场到XXX酒店,怎么过去?1)酒店接机服务,直接开车来接.2)打车过去.3)机场快轨+地铁 4)机场巴士 5)公交车 6)走路过去(不跑累死的话) 等等.使用方法,我们都可以达到从机场到XXX酒店的目的,对吧.那么我所列出的从机场到XXX酒店的的方法,就是我们可以选择的策略. 再举个例子,就是我们使用WCF时,往往避免不了对它进行扩展,例如授权,我们可以通过自定义授权来扩展WCF.这里我们可以通过自定义AuthorizationPol

  • C#中委托和事件在观察者模式中的应用实例

    通常来说当一个被监视对象的方法执行会触发观察者Observer的方法的时候,我们就可以在被监视对象中声明委托和事件.本文就以实例形式展示了C#中实现委托和事件在观察者模式中的应用.具体如下: 示例如下: 有一个宠物追踪器挂宠物身上,只要宠物离开主人100米之外,主人手上的显示器显示警告信息并声音报警. class Program { static void Main(string[] args) { PetTracker tracker = new PetTracker(); tracker.I

  • C#设计模式之Observer观察者模式解决牛顿童鞋成绩问题示例

    本文实例讲述了C#设计模式之Observer观察者模式解决牛顿童鞋成绩问题.分享给大家供大家参考,具体如下: 一.理论定义 观察者模式 描述了 一种 一对多的关系. 当某一对象的状态发生改变时,其他对象会得到 改变的通知.并作出相应的反应. 二.应用举例 需求描述:牛顿同学的期末考试成绩(Score)出来了,各科老师都想知道自己的 学生 成绩情况! 语文老师(TeacherChinese)只关心  牛顿的语文(Chinese)成绩. 英语老师(TeacherEnglish)只关心  牛顿的英语(

  • C#观察者模式(Observer Pattern)实例教程

    本文以实例形式简单讲述了C#观察者模式,分享给大家供大家参考.具体实现方法如下: 现在假设有一个软件公司,每当有新产品推出,就把信息通知到一些客户. 把通知这个动作抽象成一个接口.代码如下所示: public interface IService { void Notif(); } 客户如果想获得通知,就需要实现以上的接口.这里的客户被看作是观察者. public class CustomerA : IService { public void Notif() { Console.WriteLi

随机推荐