C#函数式编程重构:消除缓存判断分支重复代码
C# 函数式分支重复代码重构方案
核心思路
两个分支的唯一差异是DetailsAsync接口需要的ClientUserId参数来源:
- 缓存未命中:需要调用
GetUserAsync拉取用户信息、写入缓存,再取用户ID - 缓存命中:直接读取缓存中存储的用户ID
除此之外所有业务逻辑完全一致,只需要先统一拿到合法的用户ID,再执行一次公共逻辑即可,不需要在两个分支里重复编写后续流程。
方案1:保持函数式链式调用风格(适配现有代码写法)
将用户ID的获取逻辑统一收敛为同类型返回结果,和现有MapOnFailure/DoAsync扩展方法完全适配:
// 统一收敛用户ID获取逻辑,屏蔽缓存命中/未命中的分支差异 var userIdTask = _cache.TryGetValue("ClientUserId", out string cachedUserId) // 缓存命中直接返回成功结果,适配项目自身的Result类型即可 ? Task.FromResult(Result<string, AppF>.Success(cachedUserId)) : _userM.GetUserAsync(dbData.CreateEmployeeID.ToString(), cancellationToken) .MapOnFailure(failure => new AppF(failure.Message, failure.Exception)) // 拉取到用户后先写入缓存 .DoAsync(user => _cache.Set("ClientUserId", user.ClientUserId, cacheExpiration)) // 统一返回用户ID字符串,和缓存命中的返回类型对齐 .Map(user => user.ClientUserId); // 拿到用户ID后执行一次公共业务逻辑,无重复代码 var userIdResult = await userIdTask; await userIdResult.DoAsync(async clientUserId => { var opsRequest = new OperationStatusRequest(); var results = await _monitoringService.LogHttpRequestResponseAsync( opsRequest, JsonSerializer.Serialize(cRequest), async () => await _client.DetailsAsync(clientUserId, cancellationToken) .DoAsync(async _ => { // 公共成功处理逻辑 }) .DoOnFailureAsync(async _ => { // 公共失败处理逻辑 }) ); // 后续对results的处理统一写在这里 });
注:
Result<string, AppF>.Success是函数式错误处理的标准构造方法,如果使用LanguageExt等第三方库、或是项目自定义的Result类型,对应调整成功返回的构造语法即可,不影响整体逻辑。
方案2:直白流程控制写法(可读性更高,无额外类型适配成本)
如果不需要保持全程链式调用,可以先通过分支拿到用户ID,再执行后续公共逻辑,代码更直观:
string clientUserId; // 分支仅处理用户ID获取和缓存写入,不掺杂业务逻辑 if (!_cache.TryGetValue("ClientUserId", out clientUserId)) { var userResult = await _userM.GetUserAsync( dbData.CreateEmployeeID.ToString(), cancellationToken) .MapOnFailure(failure => new AppF(failure.Message, failure.Exception)); // 错误提前返回,和原有逻辑的错误处理行为一致 if (userResult.IsFailure) return userResult.Error; clientUserId = userResult.Value.ClientUserId; _cache.Set("ClientUserId", clientUserId, cacheExpiration); } // 后续所有公共业务逻辑只编写一次 var opsRequest = new OperationStatusRequest(); var results = await _monitoringService.LogHttpRequestResponseAsync( opsRequest, JsonSerializer.Serialize(cRequest), async () => await _client.DetailsAsync(clientUserId, cancellationToken) .DoAsync(async _ => { // 公共成功处理逻辑 }) .DoOnFailureAsync(async _ => { // 公共失败处理逻辑 }) );
优化说明
- 两种方案都完全消除了重复代码,后续修改
DetailsAsync调用后的业务逻辑时只需要改一处 - 将缓存判断、用户拉取、缓存写入的逻辑和核心业务逻辑解耦,职责划分更清晰
- 修正了原代码中
operationStatusRequest和实例化变量opsRequest命名不一致的笔误
内容的提问来源于stack exchange,提问作者Joseph
相关产品推荐
相关产品推荐

