详解观察者模式

在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。

在软件世界也是这样,例如,Excel 中的数据与折线图、饼状图、柱状图之间的关系;MVC 模式中的模型与视图的关系;事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。

使用场景

观察者模式所做的工作其实就是在解耦,让耦合的双方都依赖于抽象而不是具体,从而使得各自的变化都不会影响另一边的变化。

当一个对象的改变需要改变其他对象的时候,而且它不知道具体有多少对象有待改变的时候,应该考虑使用观察者模式。

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

举例

public interface ISubject
{
   void Notify();

   string SubjectState { get; set; }
}
public class Boss : ISubject
{
   private readonly IList _observers = new List();

   public void Attach(Observer observer)
   {
       _observers.Add(observer);
   }

   public void Detach(Observer observer)
   {
       _observers.Remove(observer);
   }

   public void Notify()
   {
       foreach (var observer in _observers)
       {
           observer.Update();
       }
   }

   public string SubjectState { get; set; }
}

public abstract class Observer
{
   protected string Name;
   protected ISubject Subject;

   protected Observer(string name, ISubject subject)
   {
       Name = name;
       Subject = subject;
   }

   public abstract void Update();
}
public class StockObserver : Observer
{
   public StockObserver(string name, ISubject subject) : base(name, subject)
   {
   }

   public override void Update()
   {
       Console.WriteLine($"{Name} {Subject.SubjectState} 关闭股票行情,继续工作");
   }
}
public class NBAObserver : Observer
{
   public NBAObserver(string name, ISubject subject) : base(name, subject)
   {
   }

   public override void Update()
   {
       Console.WriteLine($"{Name} {Subject.SubjectState} 关闭 NBA 直播,继续工作");
   }
}


var boss = new Boss();
var stockObserver = new StockObserver("魏关姹", boss);
var nbaObserver = new NBAObserver("易管查", boss);

boss.Attach(stockObserver);
boss.Attach(nbaObserver);

boss.Detach(stockObserver);

boss.SubjectState = "老板我胡汉三回来了";
boss.Notify();

借助 event(委托) 我们可以实现可以灵活的观察者模式,我们定义了一个新老板来演示事件的方式,来看下面的示例:

public class NewBoss : ISubject
{
   public event Action Update;

   public void Notify()
   {
       Update?.Invoke();
   }

   public string SubjectState { get; set; }
}

public class GamePlayerObserver
{
   private readonly string _name;
   private readonly ISubject _subject;

   public GamePlayerObserver(string name, ISubject subject)
   {
       _name = name;
       _subject = subject;
   }

   public void CloseGame()
   {
       Console.WriteLine($"{_name} {_subject.SubjectState} 关闭 LOL 游戏,继续工作");
   }
}

var newBoss = new NewBoss();
var stockObserver = new StockObserver("魏关姹", boss);
var nbaObserver = new NBAObserver("易管查", boss);
var gameObserver = new GamePlayerObserver("西门", newBoss);

// 注册通知事件
newBoss.Update += stockObserver.Update;
newBoss.Update += nbaObserver.Update;
newBoss.Update += gameObserver.CloseGame;

newBoss.Update -= stockObserver.Update;

newBoss.SubjectState = "老板我胡汉三回来了";
newBoss.Notify();

从上面这个示例可以看到,通过事件的方式,我们可以不要求显示继承于 Observer 这个抽象类,可以更加灵活,扩展性更强,这也是很多类库中会使用事件来扩展的重要原因

More

设计模式要干的事情就是解耦。创建型模式是将创建和使用代码解耦,结构型模式是将不同功能代码解耦,行为型模式是将不同的行为代码解耦,具体到观察者模式,它是将观察者和被观察者代码解耦。

根据应用场景的不同,观察者模式会对应不同的代码实现方式:有同步阻塞的实现方式,也有异步非阻塞的实现方式;有进程内的实现方式,也有跨进程的实现方式。

在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。不过,在实际的项目开发中,这两种对象的称呼是比较灵活的,有各种不同的叫法,比如:Subject-Observer、Publisher-Subscriber、Producer-Consumer、EventEmitter-EventListener、Dispatcher-Listener。不管怎么称呼,只要应用场景符合刚刚给出的定义,都可以看作观察者模式。

EventBus(事件总线) 就是一个观察者模式的实际应用。

原创文章,作者:晴川运维,如若转载,请注明出处:https://baike.qcidc.com/5739.html

(0)
晴川运维晴川运维
上一篇 2025年6月8日
下一篇 2025年6月8日

相关推荐

  • Swift中自定义操作符

    自定义操作符和操作符重载是一个非常强大的功能,可以让我们构建非常有趣的解决方案。它可以让我们降低呈现型函数调用的冗长,这可能会给我们清洁代码 前言 很少有Swift功能能和使用自定…

    Linux系统 2025年6月29日
  • Ubuntu下查看路由表具体方法

    路由表的主要作用是供路由器查找目标网络,进而确定转发接口及下一跳路由,完成数据包的转发功能,在本篇文章中将为大家讲解一下如何在Ubuntu中查看网络路由表的具体方法。 什么是Lin…

    Linux系统 2025年6月4日
  • Linux下应该以这样的方式运行jar包

    当需要把在Windows上开发的Java程序用在Linux上运行时,就需要吧该Java程序打包成jar包上传到Linux上去运行。 首先想到的可能就是通过 java -jar xx…

    Linux系统 2025年6月8日
  • 详解Git-安装

    Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本…

    Linux系统 2025年6月9日
  • 详解日志分析工具GoAccess

    GoAccess 是一款开源的且具有交互视图界面的实时 Web 日志分析工具,通过你的 Web 浏览器或者 *nix 系统下的终端程序(terminal)即可访问。能为系统管理员提…

    Linux系统 2025年6月4日
  • Linux 中TCP Wrappers访问控制

    TCP_Wrappers是一个工作在第四层(传输层)的的安全工具,对有状态连接的特定服务进行安全检测并实现访问控制,凡是包含有libwrap.so库文件的的程序就可以受TCP_Wr…

    Linux系统 2025年6月27日
  • 企业的六种数据存储合规性策略

    强调合规性的数据存储管理人员应该遵循行业机构分析师推荐的策略,其中包括采用自动化技术和匿名数据。存储专业人士如今有很多事情要做,但在隐私法规范围不断扩展的时代,他们的任务清单却在不…

    Linux系统 2025年6月9日
  • Linux下常见端口转发

    在一些实际的场景里,我们需要通过利用一些端口转发工具,比如系统自带的命令行工具或第三方小软件,来绕过网络访问限制触及目标系统,本篇文章重点为大家分享一下Linux下常见端口转发。 …

    Linux系统 2025年6月8日
  • 通过Linux源码的方式讲解一下进程

    进程process是指正在执行的程序;是程序正在运行的一个实例。它由程序指令,和从文件、其它程序中读取的数据或系统用户的输入组成。 进程的生命周期 img 进程一般处于这四种状态中…

    Linux系统 2025年6月8日
  • OpenSSL 在 Apache 和 Dovecot 下的使用(二)

    本篇中,Carla Schroder 会解释如何使用 OpenSSL 保护你的 Postfix/Dovecot 邮件服务器 在上周,作为我们 OpenSSL 系列的一部分,我们学习…

    Linux系统 2025年7月3日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注