深入了解C#设计模式之订阅发布模式

什么是Pub-Sub

发布订阅是一种设计模式,它允许应用程序组件之间进行松散耦合。
其实订阅发布设计中主要是发布者生成事件通道,用于在不了解任何订阅者存在的情况下通知订阅者。

当然委托EventHandlers和Event关键字在此事件处理机制中担任着重要的角色。下面我们来看看如何使用它们。

Pub和Sub的使用

首先我们看一个简单地订阅发布模式.

定义一个Action委托,无返回值.

namespace PubSubPattern
{
  public class Pub
  {
    public Action OnChange { get; set; }

    public void Raise()
    {
      if (OnChange != null)
      {
        //Invoke OnChange Action
        OnChange();
      }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var p = new Pub();
      p.OnChange += () => Console.WriteLine("Sub 1");

      p.OnChange += () => Console.WriteLine("Sub 2");

      p.Raise();

      Console.WriteLine("Press enter !");
      Console.ReadLine();

    }
  }
}

如上代码我们创建了一个发布者,并且我们调用委托进行创建我们匿名方法来订阅。由于委托提供了多播功能,因此我们可以OnChange属性上使用+=.

虽然说我们看着如上代码执行无误,但是程序中仍然存在一些问题,如果使用=而不是+=,那么OnChange属性中将会删除第一个订阅者。
由于OnChange是公共属性,因此该类的任何外部用户都可以进行调用p.OnChange().

使用Event关键字的发布订阅

下面我们来看看使用event关键字后的代码

  public class Pub
  {
    public event Action OnChange = delegate { };

    public void Raise()
    {
      OnChange();
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      Pub p = new Pub();
      p.OnChange += () => Console.WriteLine("Sub 1");
      p.OnChange += () => Console.WriteLine("Sub 2");
      p.Raise();
      Console.WriteLine("Press enter !");
      Console.ReadLine();
    }
  }

通过如上代码我们试着去解决我们第一处所说的问题,我们会发现使用event关键字后可以保护我们OnChange免受不必要的访问。它不允许使用=也就是说他不允许直接进行分配委托,因此我们现在可以避免使用=,从而避免应用程序不必要的麻烦。

可能大家也会发现OnChange初始化为空委托delegate{}。这样可以确保我们的OnChange永远不会为空。因为当我们其他进行对他调用的时候我们可以在代码中进行删除对他的非空检查.

使用EventHandlers的发布订阅

其实在订阅发布中,发布者和订阅者都不知道彼此的存在。有个EventHandler,它被称为消息代理或者说事件总线,发布者和订阅者都应该知道它,它接收所有传入的消息并且将它们进行转发.

因此呢,在如下片段中我们使用EventHandler而不是用Action.

public delegate void EventHandler(
  object sender,
  EventArgs e
)

默认情况下,EventHandler将发送对象和一些事件参数作为参数。

 public class MyEventArgs : EventArgs
    {
      public int Value { get; set; }

      public MyEventArgs(int value)
      {
        Value = value;
      }
    }

    public class Pub
    {
      public event EventHandler<MyEventArgs> OnChange = delegate { };
      public void Raise()
      {
        OnChange(this, new MyEventArgs(1));
      }
    }
    class Program
    {
      static void Main(string[] args)
      {
        Pub p = new Pub();
        p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
        p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
        p.Raise();
        Console.WriteLine("Press enter !");
        Console.ReadLine();
      }
    }

如上代码中通过pub类使用通用的EventHandler,它触发EventHandler OnChange时需要传递的事件参数类型,在上面代码片段中为MyArgs

事件中的异常

我们继续说一种情况.大家看如下代码片段

  public class MyEventArgs : EventArgs
  {
    public int Value { get; set; }

    public MyEventArgs(int value)
    {
      Value = value;
    }
  }

  public class Pub
  {
    public event EventHandler<MyEventArgs> OnChange = delegate { };
    public void Raise()
    {
      OnChange(this, new MyEventArgs(1));
    }
  }
  class Program
  {
    static void Main(string[] args)
    {
      Pub p = new Pub();
      p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
      p.OnChange += (sender, e) => { throw new Exception(); };
      p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
      p.Raise();
      Console.WriteLine("Press enter !");
      Console.ReadLine();
    }
  }

运行如上代码后,大家会发现第一个订阅者已经执行成功了,第二个订阅者引发了异常,而第三个订阅者未被调用.这是一个很尴尬的事情.

如果说我们觉得如上的过程不是我们预期的,我们需要手动引发事件并处理异常,这时候我们可以使用Delegate基类中定义的GetInvoctionList来帮助我们实现这些。

我们继续看如下代码

public class MyEventArgs : EventArgs
    {
      public int Value { get; set; }

      public MyEventArgs(int value)
      {
        Value = value;
      }
    }

    public class Pub
    {

      public event EventHandler<MyEventArgs> OnChange = delegate { };

      public void Raise()
      {
        MyEventArgs eventArgs = new MyEventArgs(1);

        List<Exception> exceptions = new List<Exception>();

        foreach (Delegate handler in OnChange.GetInvocationList())
        {
          try
          {
            handler.DynamicInvoke(this, eventArgs);
          }
          catch (Exception e)
          {
            exceptions.Add(e);
          }
        }

        if (exceptions.Any())
        {
          throw new AggregateException(exceptions);
        }
      }
    }
    class Program
    {
      static void Main(string[] args)
      {
        Pub p = new Pub();
        p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
        p.OnChange += (sender, e) => { throw new Exception(); };
        p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
        p.Raise();
        Console.WriteLine("Press enter !");
        Console.ReadLine();
      }
    }

Reference

https://github.com/hueifeng/DesignPatterns-Samples/tree/master/PubSubPattern

https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c

以上就是深入了解C#设计模式之订阅发布模式的详细内容,更多关于c# 订阅发布模式的资料请关注我们其它相关文章!

(0)

相关推荐

  • C#设计模式之Singleton模式

    前言 Singleton是二十三个设计模式中比较重要也比较经常使用的模式.但是这个模式虽然简单,实现起来也会有一些小坑,让我们一起来看看吧! 实现思路 首先我们看看这个设计模式的UML类图. 很清晰的可以看到,有三点是需要我们在实现这个模式的时候注意的地方. 私有化的构造器 全局唯一的静态实例 能够返回全局唯一静态实例的静态方法 其中,私有化构造器是防止外部用户创建新的实例而静态方法用于返回全局唯一的静态实例供用户使用.原理清楚了,接下来我们看看一些典型的实现方式和其中的暗坑. 实现方法 最简单

  • c#设计模式之单例模式的实现方式

    场景描述 单例模式对于我们来说一点也不模式,是一个常见的名称,单例模式在程序中的实际效果就是:确保一个程序中只有一个实例,并提供一个全局访问点,节省系统资源 单例模式无论是在实际开发中还是在软件应用中比较常见,比如,windows系统的任务管理器.IIS的HttpApplication.实际项目中的日志组件等等 实现方式 单例模式为了实现一个实例,那么只有不把实例创建暴露出去,只通过类本身来创建实例,为了实现效果,需要定义一个私有构造函数 单例模式实现方式有:饿汉式.懒汉式.双重验证式.静态内部

  • 快速学习C# 设计模式之职责链模式

    职责链模式简介及UML 职责链也叫责任链,他是一种行为型模式,它为请求创建了一个接收请求者对象的链,并将请求沿着这条链传递到目标对象去处理. 该模式最简单的实现方式就是运用里氏替换原则,对每个职责所持有的对象进行抽象,并使得每个职责对象都拥有共同的父类,通过对外提供出具有一般意义的接口. 范例 该范例,是我在对微服务中,服务发现的容错性进行处理的一种处理方案,考虑到服务发现过程中,如果注册中心宕机,那么可以使用本地文件存放的临时性信息,如果本地文件不存在,那么就直接用内容中存放的信息.在整个流程

  • C#设计模式之Facade外观模式解决天河城购物问题示例

    本文实例讲述了C#设计模式之Facade外观模式解决天河城购物问题.分享给大家供大家参考,具体如下: 一.理论定义 外观模式   把  分散的子系统,集合成一个系统,提供一站式服务. 二.应用举例 需求描述: 聂小倩 和 宁采臣是一对小富则安 的聊斋夫妻.住在比较偏远的小乡村. 今天,两人初次来到大城市广州,听说天河城提供一站式服务,不像小城市那样,买个东西  得  东奔西跑. 在一个地方,就可以买到 自己想要的衣服,电脑,鞋子,Iphone,还可以看大片, 吃冰淇淋,吃真功夫,买化妆品,珠宝首

  • C#设计模式之Builder生成器模式解决带老婆配置电脑问题实例

    本文实例讲述了C#设计模式之Builder生成器模式解决带老婆配置电脑问题.分享给大家供大家参考,具体如下: 一.理论定义 生成器模式 又叫:建造者模式,它  可以 把一个 复杂的对象,分步骤创建. 二.应用举例 需求描述:话说发工资了,打算去岗顶百脑汇  给老婆配置一台电脑. OK,坐着BRT,就出发了. 到岗顶,一美女扑面而来,面带微笑:先生,请问看中那个品牌,过来看一下嘛! 人家都开口了,盛情难却,就看下吧. 三.具体编码 1.CPU using System; using System.

  • 深入了解c# 设计模式之简单工厂模式

    简单工厂模式,需要说明的是,它并不属于GOF 23种设计模式中的一种.但它却丰富了工厂模式家族,因为其简单的思想和使用方式,也有很广泛的使用 简单工厂模式又称为静态工厂模式,它用来确定创建哪一种对象的实例.这种模式应该说是最简单最实用的工厂模式了,它将外界创建对象的逻辑收集起来,做到了对外界隔离对象的创建逻辑的目的,使外面完全的成为了对象实例的使用者,明确了职责. 不过这种模式也有着非常明显是的缺点,工厂类中集中了所有对象实例的创建逻辑,造成了功能的高内聚:另外在扩展方面,如果需要添加新的类,就

  • 深入了解C#设计模式之订阅发布模式

    什么是Pub-Sub 发布订阅是一种设计模式,它允许应用程序组件之间进行松散耦合. 其实订阅发布设计中主要是发布者生成事件通道,用于在不了解任何订阅者存在的情况下通知订阅者. 当然委托EventHandlers和Event关键字在此事件处理机制中担任着重要的角色.下面我们来看看如何使用它们. Pub和Sub的使用 首先我们看一个简单地订阅发布模式. 定义一个Action委托,无返回值. namespace PubSubPattern { public class Pub { public Act

  • js设计模式之代理模式及订阅发布模式实例详解

    本文实例讲述了js设计模式之代理模式及订阅发布模式.分享给大家供大家参考,具体如下: 为啥将两种模式放在一起呢?因为这样文章比较长啊. 写博客的目的我觉得首要目的是整理自己的知识点,进而优化个人所得知识体系.知识成为个人的知识,就在于能够用自己的话表达同一种意义. 本文是设计模式系列文章的第二篇文章,第一篇:. 1,代理模式,只是学习了虚拟代理以及缓存代理,具体案例 1)虚拟代理 //业务代码 var myImage = (function() { var imgNode = document.

  • vue3如何使用eventBus订阅发布模式

    目录 Ⅰ. 什么是eventBus? Ⅱ. vue3 如何使用 步骤一 (eventBus 容器) 步骤二 ( 订阅者 ) 步骤三 ( 发布者 ) 总结 Ⅰ. 什么是eventBus? 通俗的讲,就是在任意一个组件,想把消息(参数) -> 传递到任意一个组件 ,并执行一定逻辑. Ⅱ. vue3 如何使用 eventBus vue3中没有了,eventBus,所以我们要自己写,但是非常简单. 步骤一 (eventBus 容器) 在src目录,创建个bus文件夹,存放 自己写的 bus.js : 编

  • javascript设计模式之订阅者模式

    目录 一. 初识代理模式 二. 代理模式的实现思想 三. 代理模式分类 四. 虚拟代理模式的实际运用 五. 代理的使用意义及要求 六. 总结 一. 初识代理模式 代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问.它的用处就是当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问.通俗来讲就是,代理是一个中间人,负责在客户和卖家之间传递信息,将没用的信息过滤,将有利于交易成功的信息传递给卖家,从而加大交易的成功率. 二. 代理模式的实现思想 现在我们

  • 深入理解Java设计模式之中介者模式

    目录 一.什么是中介者模式 二.中介者模式的结构 三.中介者模式的优缺点 四.中介者模式的使用场景 五.中介者模式与发布/订阅模式的异同 六.中介者模式的实现 结果 总结 一.什么是中介者模式 用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 中介者作为一种行为设计模式,它公开一个统一的接口,系统的不同对象或组件可以通过该接口进行通信.增加一个中介者对象后,所有的相关对象通过中介者对象来通信,而不是互相引用,所以当一个

  • Redis 订阅发布_Jedis实现方法

    我想到使用Redis的订阅发布模式是用来解决推送问题的-. 对于概念性的叙述,多多少少还是要提一下的: 什么是Redis发布订阅?Redis发布订阅是一种消息通信模式,发送者通过通道A发送消息message,订阅过通道A的客户端就可以接收到消息message.嗯度娘上面的解释要比我所说的好多了,而我所理解的就是:所谓的订阅发布模式,其实和我们看电视,听广播差不多,在我们没有调台(换频道)的时候,那个频道也是在传递消息的(发布).我们换到那个频道上(订阅)就能接收到消息了.是的,虽然可能有些不恰当

  • JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例

    本文实例讲述了JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法.分享给大家供大家参考,具体如下: 观察者模式,又称为发布订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己的状态. 在观察者模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象的特定活动并在状态改变后获得通知.订阅者也称为观察者,而被观察的对象称为发布者或主题.当发生了一个重要的事件时,发布

  • JavaScript设计模式之观察者模式(发布者-订阅者模式)

    观察者模式( 又叫发布者-订阅者模式 )应该是最常用的模式之一. 在很多语言里都得到大量应用. 包括我们平时接触的dom事件. 也是js和dom之间实现的一种观察者模式. 复制代码 代码如下: div.onclick  =  function click (){ alert ( "click' ) } 只要订阅了div的click事件. 当点击div的时候, function click就会被触发. 那么到底什么是观察者模式呢. 先看看生活中的观察者模式. 好莱坞有句名言. "不要给我

  • Spring Boot ActiveMQ发布/订阅消息模式原理解析

    本文在<Spring Boot基于Active MQ实现整合JMS>的基础上,介绍如何使用ActiveMQ的发布/订阅消息模式.发布/订阅消息模式是消息发送者发送消息到主题(topic),而多个消息接收者监听这个主题:其中,消息发送者和接收者分别叫做发布者(publisher)和订阅者(subscriber),对于发布者来说,它和所有的订阅者就构成了一个1对多的关系.这种关系如下图所示: 发布/订阅模式的工作示意图 消息生产者将消息(发布)到topic中,可以同时有多个消息消费者(订阅)消费该

  • 原生js实现的观察者和订阅者模式简单示例

    本文实例讲述了原生js实现的观察者和订阅者模式.分享给大家供大家参考,具体如下: 观察者模式也叫 发布者-订阅者模式,发布者发布事件,订阅者监听事件并做出反应 在传统的前端解耦方面,观察者模式作为比较常见一种设计模式,大量使用在各种框架类库的设计当中. 核心代码: // eventProxy.js 'use strict'; const eventProxy = { onObj: {}, oneObj: {}, on: function(key, fn) { if(this.onObj[key]

随机推荐