从Void到Task<PublishAggregateResult>:一次服务方法返回类型重构的纠结与决策

news/2025/9/24 13:35:11/文章来源:https://www.cnblogs.com/yghr3a/p/19109072

今天原本想美美地完成UI层与Core层通过消息总线实现博客发布的功能。一切都很顺利,直到我重构到 PublishBlog 方法,准备为ApplicationService 写事件处理逻辑时,不然发现不对劲——“不兑!等等,我该怎么把发布结果信息传回给ApplicationService 呢?” 就是这么一个看似简单的问题,让我一脸懵逼。。。

一、 项目背景与问题起源

先简单介绍下我的项目:这是一个.NET Winform博客一键发布工具。架构上做了清晰的分层(自己突然想出来写着玩的):

  • UI层:负责界面交互,它不关心核心逻辑,只通过消息总线向Core层发送指令(比如“发布博客”)。

  • Core层:负责所有核心业务。其中有一个关键的 ApplicationService,它就像是Core层的“总指挥部”,所有消息的接收和发布都由它一手包办。当它收到UI层的指令后,会去调用像 BlogPublishService 这样的具体“业务部门”来干活。

这种设计的好处是解耦:UI层和Core层互相不认识,只通过消息沟通,非常清爽。在后续开发我甚至打算使用更加精美与符合现代审美WPF项目来作为UI层,所以如此设计打有好处。(得意叉腰)

问题就出在这里:我的 BlogPublishService.PublishBlog 方法,以前干完活是自己直接“大喊一声”(发布事件)通知UI层。但现在规矩变了,所有“喊话”的权力都收归 ApplicationService 了。这下好了,PublishBlog 方法变成了一个“哑巴”,它活干得怎么样,ApplicationService 完全不知道,自然也就没法向上汇报了。

所以,核心问题变成了:如何让“哑巴”开口,把它工作的结果告诉“总指挥部”?

二、 思路博弈:返回值 vs 输出参数

首先得确定沟通方式。脑子里蹦出两个选项:

  1. 返回值 (Return Value):最直接,像函数式编程,输入 -> 处理 -> 输出,符合直觉。

  2. 输出参数 (out Parameters):感觉更“高级”一点,但用在这里有点别扭。

我琢磨了一下,PublishBlog 这个方法非常纯粹:给它配置和内容,它就去发布,最后给你一个结果。这简直就是为返回值量身定做的场景。用 out 参数的话,代码会显得很拧巴,所以果断选择了返回值方案。


// 返回值方式:清晰,像在问一个问题并得到一个答案var result = await blogPublishService.PublishBlog(configs, blogInfo);// 输出参数方式:有点绕,像在操作一个机器blogPublishService.PublishBlog(configs, blogInfo, out var result);

三、 命名的纠结:与AI的“头脑风暴”

确定了用返回值,下一个头疼的事来了:返回什么类型?方法内部主要有两种信息要传递:一个是所有平台的发布详情 (List<PublishResult>),另一个是可能出现的业务错误 (List<string>)。

直接返回元组 (List<PublishResult>, List<string>) 太丑了,过于冗长,不够优雅,Item1Item2 过几天自己都看不懂。返回事件类型又会造成层与层之间的耦合。所以,定义一个新类型是唯一优雅的选择

但取什么名字呢?我犯了难,于是求助了AI。AI给了我几个方案:

  • PublishOperationResult (发布操作结果)

  • PublishSummaryResult (发布摘要结果)

  • PublishAggregateResult (发布聚合结果)

我盯着这几个名字看了好久。OperationResult 太泛了,什么操作都能用;SummaryResult 强调摘要,感觉有点轻量。最后我选择了 PublishAggregateResult

为什么? 因为这个词精准地描述了这个类的本质:它不是一个简单的结果,而是由多个PublishResult聚合而成的。Aggregate(聚合)这个词在DDD(领域驱动设计)里很常见,它暗示了这是一个整体,能很好地表达“总和”的概念。就这么定了!


// 最终,方法签名变得非常清晰public async Task<PublishAggregateResult> PublishBlog(...){// ... 内部逻辑return new PublishAggregateResult {PublishResults = res,FailReasons = failedReasons};}

四、 总结与心得

这次重构让我深刻体会到,软件设计就是在不断地做权衡。从最初的架构选择(消息总线),到具体的技术方案(返回值),再到最后的命名细节,每一步都是一个决策点。

  1. 分层与解耦是基础:清晰的分层让问题暴露得更早,解决起来方向也更明确。

  2. 返回值是直觉之选:对于纯计算或纯业务逻辑,返回值通常是最直接、最清晰的通信方式。

  3. 命名是设计的延伸:一个好名字能准确传达类的职责,PublishAggregateResult 无疑比 Item1Item2 要好懂得多。

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

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

相关文章

LVGL移植到STM32F4出现无法运行的问题

跟着网上的教程一步步移植LVGL v8.3到STM32F407VET6上,虽然能成功运行,但是在刷新屏幕可能会出现:只刷新了一部分屏幕 整个屏幕都会卡死查阅了很多资料都没解决,在使用别人的Keil工程时,发现代码的优化等级是-O1,…

网站建设的原因有什么给个网址2021年能看的

环境 Windows 11 家庭中文版git version 2.41.0.windows.1 问题情况 在使用 “命令行终端” 和 “Git Bash” 在本地Git仓库敲击命令时&#xff0c;对中文名称文件显示一连串的数字&#xff0c;如下所示&#xff1a;这种情况通常是由于字符编码设置不正确所引起的 解决办法 设置…

如何选择做网站网易企业邮箱忘记密码

&bc_control spec_bdy_width 此参数指定用于边界过渡的格点总行数&#xff0c;默认值为5。此参数只用于真实大气方案。参数的大小至少为spec_zone 和 relax_zone的和。 spec_zone 指定区域(specified zone)的格点数&#xff0c;默认值为 1。指定边条件时起作用。 relax…

网站地图提交入口班级优化大师电脑版

1,先把报错SQL语句拿出来执行&#xff0c;看看是不是报的这个错 ORA-01830: 日期格式图片在转换整个输入字符串之前结束 2&#xff0c;然后查看默认日期格式是不是“YYYY-MM-DD HH24:MI:SS”&#xff08;正确格式&#xff09;。&#xff1b; 执行&#xff1a; SELECT * FRO…

题目记录(Before NOIP2025 ver)

T1. Beautiful Sequence Unraveling 定义 \(dp_{i,j}\) 表示长度为 \(i\),值域在 \([1,j]\) 之间的好序列的个数。发现好序列不好刻画,所以转化为所有序列的数量减去不是好序列的数量。前者很显然,即 \(i^j\)。接下…

专业修复sqlserver master 数据库损坏。

例如一下错误 在重做数据库“master”中的日志记录操作时,日志记录 ID (57081:184:2) 出错。通常,特定故障以前会在操作系统错误日志中记录为错误。请从完整备份还原数据库,或者修复该数据库。 无法在数据库“maste…

78-材料可视化-折线图

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

jenkins job的configure中配置git时 选择的credential为什么不能选择secret认证方式的数据

你在 Jenkins 中配置 Git 时,无法直接选择 "Secret text" 类型的凭证来认证代码仓库,这主要是因为 Git 操作(特别是通过 HTTPS 克隆时)通常需要的是“用户名+密码”组合,而单纯的 Secret text(如 GitH…

Day21继承

继承可实现代码的复用,子类获得父类的属性与方法,且可在此基础上拓展package oop1.Demo5; //在java中,所有的类,都直接或间接继承object //人 父类 public class person /*extends Object*/{public int getMon…

C# Avalonia 15- Animation- ImageWipe

C# Avalonia 15- Animation- ImageWipe在上一个AnimationPlayer例子上进行扩展,让其具备完整的小型动画功能。 AnimationPlayer类public partial class AnimationPlayer : ObservableObject{// ---------------------…

实用指南:科研绘图Origin百度云盘下载与安装指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

大庆免费网站建设开发建设网站需要什么人才

自动接收图片并上传到服务器&#xff0c;仅适用企业微信应用 前言 WorkTool企微机器人可以接收客户群的消息&#xff0c;但接收图片一直是个问题&#xff0c;前面也介绍过两种图片接收方案&#xff0c;但都会影响运行效率&#xff0c;并且不能达到100%的图片接收率&#xff0…

题解:P8067 [BalkanOI 2012] balls

题意 给出一个长为 \(n\) 的序列,让你选择一段长度 \(\ge 2\) 区间内所有值变为区间右或左端点的值,最大化操作后的权值和。 思路 以第一问为例,选择一段区间 \((l,r]\) 后权值和的变化量为: \[(r-l)\times a_r-(s…

题解:P8300 [COCI 2012/2013 #2] INSPEKTOR

题意 要求维护一个直线序列,支持以下操作:操作 \(1\),在 \(K\) 这个位置用一条直线 \(y=Zx+S-Z\times T\) 覆盖这个点原来的直线。 操作 \(2\),查询区间 \([A,B]\) 内的直线在 \(T\) 处的最大值。思路 看到加入直线…

SuperHarness-3D低压柜机电协同设计方案!

【引领未来,智控电气新纪元】 在电力与创新的交响乐章中,利驰软件携手SolidWorks平台,为您匠心打造——低压柜机电协同设计方案,开启电气系统智能化的全新篇章!🌟 智绘蓝图,精准协同 🌟 想象一下,当SolidWo…

详细介绍:.NET驾驭Word之力:打造专业文档 - 页面设置与打印控制完全指南

详细介绍:.NET驾驭Word之力:打造专业文档 - 页面设置与打印控制完全指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family…

vim 入门教学4(命令行模式教学)

vim 入门教学4(命令行模式教学)normal模式下的命令行模式 在vim中除了normal模式能进入命令行模式,也可以在visual模式下进入。两者有所不同visual模式下进入会自动识别范围 normal模式进入命令行模式: 单次进入命令行…

制作个人免费网站展示设计设计学校

在 C 中&#xff0c;operator"" 是用户定义字面量&#xff08;User-Defined Literals&#xff09;的一部分&#xff0c;它允许程序员扩展现有的字面量类型或者创建新的字面量类型。用户定义字面量是在 C11 标准中引入的特性&#xff0c;主要用于提供更易读、更具表达…

使用.NET标准库实现多任务并行处理的详细过程 - 实践

使用.NET标准库实现多任务并行处理的详细过程 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&quo…

下载类网站做多久才有流量郴州网红

4000 还是E2140&#xff1f;两大人气CPU对决互联网 发布时间&#xff1a;2009-04-21 01:31:37 作者&#xff1a;佚名 我要评论今夏攒机&#xff0c;双核处理器无疑是网友们的第一选择。由于Intel和AMD的大力推广&#xff0c;双核处理器的价格目前已经跌到了一个大众消费…