C#中的事件聚合器实现方法

news2024/10/7 10:25:33

概述:_对象之间的关系_是使代码库难以理解和难以维护的原因。为了更好地理解它,我们求助于马丁·福勒(Martin Fowler):事件聚合器是间接的简单元素。在最简单的形式中,您可以让它注册到您感兴趣的所有源对象,并让所有目标对象注册到事件聚合器。事件聚合器通过将源对象中的任何事件传播到目标对象来响应该事件。事件聚合器有很多好处。在本文中,我将展示事件聚合器如何使我们更容易扩展应用程序。给我一个理由!为了展示它如何使我们的代码更易于理解,请查看以下模型:public class User { public string Id { get; set; } public bool IsMarried_对象之间的关系_是使代码库难以理解和难以维护的原因。

想了解更多游戏开发知识,可以扫描下方二维码,免费领取游戏开发4天训练营课程

为了更好地理解它,我们求助于马丁·福勒(Martin Fowler):

图片

事件聚合器是间接的简单元素。在最简单的形式中,您可以让它注册到您感兴趣的所有源对象,并让所有目标对象注册到事件聚合器。事件聚合器通过将源对象中的任何事件传播到目标对象来响应该事件。

事件聚合器有很多好处。在本文中,我将展示事件聚合器如何使我们更容易扩展应用程序。

给我一个理由!

为了展示它如何使我们的代码更易于理解,请查看以下模型:

public class User {
  public string Id { get; set; }
  public bool IsMarried { get; set; }
}
public class Resume {
  public string Description { get; set; }
  public string UserId { get; set; }
  public bool IsUserMarried { get; set; }
}
public class Store {
  public string Title { get; set; }
  public string OwnerId { get; set; }
  public bool IsOwnerMarried { get; set; }
}

在这里,并将用户的当前婚姻状况存储在一个名为 的字段中。假设每个实体都是一个聚合根,因此每个实体都有自己的服务:ResumeStoreIsUserMarried

public class ResumeService {
  private readonly ICollection<Resume> db = new List<Resume> {
    new Resume {
      Description = "My current resume",
      UserId = "1",
      IsUserMarried = false
    }
  };
  public void SetMaritalStatus(string userId, bool isMarried) {
    foreach (var resume in db.Where(a => a.UserId.Equals(userId))) {
      resume.IsUserMarried = isMarried;
    }
  }
}
public class StoreService {
  private readonly ICollection<Store> db = new List<Store> {
    new Store {
      Title = "Restaurant",
      OwnerId = "1",
      IsOwnerMarried = false
    }
  };
  public void SetMaritalStatus(string ownerId, bool isMarried) {
    foreach (var store in db.Where(a => a.OwnerId.Equals(ownerId))) {
      store.IsOwnerMarried = isMarried;
    }
  }
}
public class UserService {
  private readonly ICollection<User> db = new List<User> {
    new User {
      Id = "1",
      IsMarried = false
    }
  };
  private readonly ResumeService resumeService;
  private readonly StoreService storeService;
  
  public UserService(ResumeService resumeService, 
    StoreService storeService) {
    this.resumeService = resumeService;
    this.storeService = storeService;
  }
  public void GotMarried(string userId) {
    var user = db.First(a => a.Id.Equals(userId));
    user.IsMarried = true;
  
    // propagate changes to other parts of the code
    resumeService.SetMaritalStatus(userId, true);
    storeService.SetMaritalStatus(userId, true);
  }
}

ResumeService并且两者都有一个更新用户婚姻状况的方法( )。正如你所看到的,对这两个服务都有依赖性,因为当一个用户结婚时,想要通知其他服务。此代码有效,但有两个缺点:StoreServiceSetMaritalStatusUserServiceUserService

1-实际上不依赖或执行其操作!=>(假依赖关系)UserServiceResumeServiceStoreService

2-每当我们添加存储用户婚姻状况的新实体时,我们必须记住更新!=>(难以扩展)GotMarriedUserService

解决方案:事件聚合器

与其引入依赖项(其他服务),不如调整定义一个事件:UserService

public class MaritalStatusChanged : IEvent {  
  public MaritalStatusChanged(string userId, bool isMarried) {  
    UserId = userId;  
    IsMarried = isMarried;  
  }  
  public string UserId { get; }  
  public bool IsMarried { get; }  
}

然后我们需要更新.首先删除依赖项,然后更新方法:UserServiceGotMarried

public class UserService {  
  private readonly ICollection<User> db = new List<User> {  
    new User {  
      Id = "1",  
      IsMarried = false  
    }  
  };  
  private readonly IEventEmitter eventEmitter
  public UserService(IEventEmitter eventEmitter) {  
    this.eventEmitter = eventEmitter;  
  }  
  public void GotMarried(string userId) {  
    var user = db.First(a => a.Id.Equals(userId));  
    user.IsMarried = true;  
    
    // propagate changes to other parts of the code  
     eventEmitter.Publish(new MaritalStatusChanged(userId, true)); 
  }  
}

所以现在,它只取决于事件发射器。活动发射器是我们的活动总线!它在整个域中发布事件。现在,如果想要了解此事件,我们只需创建一个处理程序。例如,这是一个添加到正文中的处理程序:ResumeService

public class MaritalStatusChangedHandler :  
 IEventHandler<MaritalStatusChanged> {  
  private readonly ResumeService service;  
  public MaritalStatusChangedHandler(ResumeService service) {  
    this.service = service;  
  }  
  public Task Handle(MaritalStatusChanged ev) {  
    service.SetMaritalStatus(ev.UserId, ev.IsMarried); 
    return Task.CompletedTask;  
  }  
}

将它们粘在一起:

// 1- create an event bus
var bus = new DefaultEventBus();
// 2- create services
var userService = new UserService(bus);
var resumeService = new ResumeService();
var storeService = new StoreService();
// 3- subscribe
bus.Subscribe<MaritalStatusChanged, ResumeService.MaritalStatusChangedHandler>(
  new ResumeService.MaritalStatusChangedHandler(resumeService));
bus.Subscribe<MaritalStatusChanged, StoreService.MaritalStatusChangedHandler>(
  new StoreService.MaritalStatusChangedHandler(storeService));
// 4- someone got married
userService.GotMarried("1");

1- 这将创建事件总线。事件总线实现 IEventEmitter 和 IEventSink。 发布事件并允许您订阅事件。

完整代码:

using libc.eventbus.System;
using libc.eventbus.Types;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace libc.eventbus.tests
{
  [TestClass]
  public class Showcase2
  {
    [TestMethod]
    public void Showcase()
    {
      // 1- create an event bus
      var bus = new DefaultEventBus();

      // 2- create services
      var userService = new UserService(bus);
      var resumeService = new ResumeService();
      var storeService = new StoreService();

      // 3- subscribe
      bus.Subscribe<MaritalStatusChanged, ResumeService.MaritalStatusChangedHandler>(
          new ResumeService.MaritalStatusChangedHandler(resumeService));

      bus.Subscribe<MaritalStatusChanged, StoreService.MaritalStatusChangedHandler>(
          new StoreService.MaritalStatusChangedHandler(storeService));

      // 4- someone got married
      userService.GotMarried("1");
    }

    public class UserService
    {
      private readonly ICollection<User> _db = new List<User>
            {
                new User
                {
                    Id = "1",
                    IsMarried = false
                }
            };

      private readonly IEventEmitter _eventEmitter;

      public UserService(IEventEmitter eventEmitter)
      {
        _eventEmitter = eventEmitter;
      }

      public void GotMarried(string userId)
      {
        var user = _db.First(a => a.Id.Equals(userId));
        user.IsMarried = true;

        // propagate changes to other parts of the code
        _eventEmitter.Publish(new MaritalStatusChanged(userId, true));
      }
    }

    public class ResumeService
    {
      private readonly ICollection<Resume> _db = new List<Resume>
            {
                new Resume
                {
                    Description = "My current resume",
                    UserId = "1",
                    IsUserMarried = false
                }
            };

      public void SetMaritalStatus(string userId, bool isMarried)
      {
        foreach (var resume in _db.Where(a => a.UserId.Equals(userId))) resume.IsUserMarried = isMarried;

        Console.WriteLine($"{userId} is {(isMarried ? "married" : "single")} now");
      }

      public class MaritalStatusChangedHandler : IEventHandler<MaritalStatusChanged>
      {
        private readonly ResumeService _service;

        public MaritalStatusChangedHandler(ResumeService service)
        {
          _service = service;
        }

        public Task Handle(MaritalStatusChanged ev)
        {
          _service.SetMaritalStatus(ev.UserId, ev.IsMarried);

          return Task.CompletedTask;
        }
      }
    }

    public class StoreService
    {
      private readonly ICollection<Store> _db = new List<Store>
            {
                new Store
                {
                    Title = "Restaurant",
                    OwnerId = "1",
                    IsOwnerMarried = false
                }
            };

      public void SetMaritalStatus(string userId, bool isMarried)
      {
        foreach (var store in _db.Where(a => a.OwnerId.Equals(userId))) store.IsOwnerMarried = isMarried;

        Console.WriteLine($"{userId} is {(isMarried ? "married" : "single")} now");
      }

      public class MaritalStatusChangedHandler : IEventHandler<MaritalStatusChanged>
      {
        private readonly StoreService _service;

        public MaritalStatusChangedHandler(StoreService service)
        {
          _service = service;
        }

        public Task Handle(MaritalStatusChanged ev)
        {
          _service.SetMaritalStatus(ev.UserId, ev.IsMarried);

          return Task.CompletedTask;
        }
      }
    }

    public class MaritalStatusChanged : IEvent
    {
      public MaritalStatusChanged(string userId, bool isMarried)
      {
        UserId = userId;
        IsMarried = isMarried;
      }

      public string UserId { get; }
      public bool IsMarried { get; }
    }

    public class User
    {
      public string Id { get; set; }
      public bool IsMarried { get; set; }
    }

    public class Resume
    {
      public string Description { get; set; }
      public string UserId { get; set; }
      public bool IsUserMarried { get; set; }
    }

    public class Store
    {
      public string Title { get; set; }
      public string OwnerId { get; set; }
      public bool IsOwnerMarried { get; set; }
    }
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1707288.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(MES与ERP系统的区别和联系)

MES与ERP系统的区别和联系 MES制造执行系统&#xff0c;是一套面向制造公司车间执行层的生产信息化管理系统。MES 可觉得公司提供涉及制造数据管理、计划排产管理、生产调度管理、库存管理、质量管理、人力资源管理、工作中心、设备管理、工具工装管理、采购管理、成本管理、项…

C++牛客周赛43题目分享(3)小红平分糖果,小红的完全平方数,小苯的字符串变化,小红的子数组排列判断

目录 ​编辑 1.前言 2.四道题目 2.1小红平分糖果 2.1.1题目描述 2.1.2输入描述 2.1.3输出描述 2.1.4示例 2.1.5代码 2.2小红的完全平方数 2.1.1题目描述 2.1.2输入描述 2.1.3输出描述 2.1.4示例 2.1.5代码 2.3小苯的字符串变化 2.1.1题目描述 2.1.2输入描述 …

Steam游戏搬砖:靠谱吗,详细版说下搬砖中的核心内容!

可能大家也比较关注国外Steam游戏搬砖这个项目&#xff0c;最近单独找我了解的也比较多&#xff0c;其实也正常&#xff0c;因为现在市面上的项目很多都很鸡肋&#xff0c;而且很多都是一片红海&#xff0c;内卷太过严重&#xff0c;所以对于Steam的关注度也高很多&#xff0c;…

CTF-web-WP-攻防世界-1

1、view_source&#xff1a;打开开发者工具F12就能看到flag 2、robots (1)、使用目录扫描工具 工具&#xff1a;dirsearch命令&#xff1a;python dirsearch.py -u http://61.147.171.105:55644/ -e * (2)、看到下面有robots.txt文件&#xff0c;在URL后面输入&#xff0c…

torch.matmul()的用法

这篇文章记录torch.matmul()的用法 这里仿照官方文档中的例子说明&#xff0c;此处取整数随机数&#xff0c;用于直观的查看效果&#xff1a; vector x vector 两个一维向量的matmul相当于点积&#xff0c;得到一个标量 tensor1 torch.randint(1, 6, (3,)) tensor2 torch.…

SYD881X HID工程重连后连接参数没有更新功耗下不来

SYD881X HID工程重连后连接参数没有更新功耗下不来 现在测试到一个问题,第一次连接上的时候过一段时间功耗会下来到100UA以内,这个是正常的,但是关掉手机蓝牙再打开手机蓝牙就发现功耗是500UA左右下不来了! 抓包发现第一次连接和重连的时候手机给的连接参数是一样的: 问题是当…

nginx设置一个TCP代理(用于RDP连接)

在 Nginx 中&#xff0c;stream 上下文必须在 http 上下文之外&#xff0c;并且只能位于顶级配置文件中或包含在主配置文件中的单独文件中。 Nginx 的配置文件通常包含一个 http 块&#xff0c;用于处理 HTTP 和 HTTPS 请求&#xff0c;但是 stream 块是独立的&#xff0c;并且…

微信好友,如此的陌生,渐渐都成了只是人名!也许没有利益关系导致!

微信里一直聊天聊的挺好的朋友&#xff0c;不知怎么到后来却联系少了&#xff0c;最后渐渐的变成躺在微信备注里的一个陌生朋友&#xff01; 以前通过工作认识了一个朋友&#xff0c;初次见面的印象不是很深刻了&#xff0c;只记得当时给我的印象是对方很有礼貌&#xff0c;特别…

段位在于面对人性之恶,一笑而过

这个小哥哥不知道是哪里不对劲了&#xff0c;突然给我留言说我在骗流量&#xff0c;骗关注。公众号是我的&#xff0c;文章是我写的&#xff0c;主要分享的就是我创业的一些接单案例&#xff0c;因为之前收到很多无效的留言&#xff0c;寻求合作就几个字我不想接收无效信息&…

【Android14 ShellTransitions】(一)开篇

说来惭愧&#xff0c;AndroidU都已经开发这么久了&#xff0c;但是我还没有整理过ShellTransition相关的知识。我本来希望能够系统的写一篇关于ShellTransition的笔记出来&#xff0c;但是发现一来这是一个比较庞大的模块&#xff0c;二来我个人能力有限&#xff0c;对ShellTra…

一文搞懂oracle事务提交以及脏数据落盘的原则

本文基于oracle 19c 做事务提交以及oracle脏数据落盘的相关解读 第一章 相关进程及组件介绍&#xff1a; 1.LGWR&#xff1a; 重做日志条目在系统全局区域 &#xff08;SGA&#xff09; 的重做日志缓冲区中生成。LGWR 按顺序将重做日志条目写入重做日志文件。如果数据库具有…

Jenkins安装启动(JDK8/11/17)

目录 传送门前言一、概念1、Jenkins是什么2、Jenkins 的安装概念图 二、下载1、下载war包&#xff08;JDK17环境推荐&#xff09;2、下载rpm安装包&#xff08;JDK8环境推荐&#xff09; 三、对应JDK8的实际安装&#xff08;Jenkins2.346.1&#xff09;1、安装指令2、Jenkins2.…

Java入门基础学习笔记46——String的案例

完成用户登录功能&#xff1a; 需求&#xff1a; 系统的正确的用户名和密码&#xff1a;changchunhua/123456&#xff0c;请在控制台开发一个登录界面&#xff0c;接收用户输入的登录名和密码&#xff0c;判断用户是否登录成功&#xff0c;登录成功后展示&#xff1a;“欢迎进…

2024HBCPC:E Breakfast II

题目描述 作为一个合格的大学生&#xff0c;你不仅需要学习成绩好&#xff0c;还需要会买包子和鸡蛋。 今天&#xff0c;又轮到你们给你的导师买早饭了&#xff01; 这一次你们一共需要给导师买 n n n 个包子和 m m m 个鸡蛋&#xff08;请注意&#xff0c;这一次可能不再只…

【class18】人工智能初步----语音识别(4)

【class17】 上节课&#xff0c;我们学习了: 语音端点检测的相关概念&#xff0c;并通过代码切分和保存了音频。 本节课&#xff0c;我们将学习这些知识点&#xff1a;1. 序列到序列模型2. 循环神经网络3. 调用短语音识别接口 知其然&#xff0c;知其所以然 在调用语…

JS——对象

1.什么是对象 对象是什么&#xff1f; 对象是一种数据类型 无序的数据的集合&#xff08; 数组是有序的数据集合 &#xff09; 对象有什么特点&#xff1f; 无序的数据的集合 可以详细地描述某个事物 静态特征 (姓名, 年龄, 身高, 性别, 爱好) > 可以使用数字, 字符串…

数据结构算法题day03

数据结构算法题day03 题目 题目 2.设计一个高效算法&#xff0c;将顺序表L的所有元素逆置&#xff0c;要求算法的空间复杂度为O(1)算法思想&#xff1a; 1、常规的解法&#xff1a; Void reverse (sqlist &L){Elemtype temp; //辅助变量for(i 0,i < L.length; i){temp…

985上交应届生转正12天,被某东辞退了!

&#x1f447;我的小册 45章教程:(小白零基础用Python量化股票分析小册) ,原价299&#xff0c;限时特价2杯咖啡&#xff0c;满100人涨10元。 01.事情起源 最近粉丝群都在转发一个截图&#xff0c;某应届毕业生在某东实习一年&#xff0c;才转正才12天&#xff0c;就因为自己调侃…

kafka-消费者组-发布订阅测试

文章目录 1、发布订阅测试1.1、创建消费者4并指定组 my_group21.2、列出所有的消费者组1.3、查看 my_group2 组的详细信息1.4、发送第六条消息accomplish1.4.1、查看 my_group1 组的详细信息1.4.2、查看 my_group2 组的详细信息 1、发布订阅测试 接着上一篇点对点博客测试 kafk…

【RabbitMQ】使用SpringAMQP的Publish/Subscribe(发布/订阅)

Publish/Subscribe **发布(Publish)、订阅(Subscribe)&#xff1a;**允许将同一个消息发送给多个消费者 **注意&#xff1a;**exchange负责消息路由&#xff0c;而不是存储&#xff0c;路由失败则消息丢失 常见的**X(exchange–交换机)***类型&#xff1a; Fanout 广播Direc…