首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >埋葬这9种过时设计模式!.NET 10 + C#12 的现代化重构指南

埋葬这9种过时设计模式!.NET 10 + C#12 的现代化重构指南

作者头像
郑子铭
发布2025-08-24 10:59:45
发布2025-08-24 10:59:45
7600
代码可运行
举报
运行总次数:0
代码可运行

曾几何时,某些设计模式是 .NET 优秀架构的基石。我们为 DbContext 包装仓储层,为缓存构建装饰器,精心设计线程安全的单例类。这些模式确实解决过问题——至少在当年如此。

但 .NET 已颠覆游戏规则。随着高级依赖注入、源生成器、Minimal API 和 C#12 新特性的到来,许多经典模式悄然过时。并非它们有错,而是 .NET 已替你处理了这些关切。


1. 仓储层 + 工作单元

传统模式

代码语言:javascript
代码运行次数:0
运行
复制
public interface IRepository<T>  // 冗余抽象
{
    Task<T> GetByIdAsync(int id);
    void Add(T entity);
    void Remove(T entity);
}
public interface IUnitOfWork  // 重复造轮子
{
    Task CommitAsync();
}

过时原因: EF Core 已通过 DbSet<T> 暴露仓储逻辑,并通过 SaveChangesAsync 管理工作单元。自定义包装器增加无意义抽象层。

现代方案

代码语言:javascript
代码运行次数:0
运行
复制
public class ProductService(MyContext _db)  // 直接注入DbContext
{
    public Task<Product?> GetByIdAsync(int id) => 
        _db.Products.FindAsync(id).AsTask();
    
    public async Task AddProduct(Product product!)
    {
        _db.Products.Add(product);
        await _db.SaveChangesAsync();  // 原生工作单元
    }
}

结合 C#12 必需成员与空检查:

代码语言:javascript
代码运行次数:0
运行
复制
public record Product
{
    public required string Name { get; init; }  // 编译时安全
}

2. 服务定位器

反例

代码语言:javascript
代码运行次数:0
运行
复制
var logger = ServiceLocator.Get<ILogger>();  // 隐藏依赖

过时原因: 静态访问使依赖不可见,测试如同噩梦。

现代方案

代码语言:javascript
代码运行次数:0
运行
复制
// Minimal API 显式注入
app.MapGet("/log", (ILogger<MyService> logger) => 
    logger.LogInformation("优雅的日志记录"));

// 构造函数注入
public class AuthService(ILogger<AuthService> _logger)
{
    public void Authenticate() => 
        _logger.LogInformation("用户认证中...");
}

3. 日志包装器

传统写法

代码语言:javascript
代码运行次数:0
运行
复制
public class LoggerHelper  // 手工包装器
{
    private readonly ILogger _logger;
    public void LogFailedLogin(string userId) =>
        _logger.LogWarning($"登录失败: {userId}");  // 字符串拼接低效
}

现代方案

代码语言:javascript
代码运行次数:0
运行
复制
public static partial class AuthLog  // 源生成器
{
    [LoggerMessage(EventId = 101, Level = LogLevel.Warning,
        Message = "用户 {UserId} 登录失败")]
    public static partial void FailedLogin(ILogger logger, string userId);
}

// 调用:零分配开销
AuthLog.FailedLogin(_logger, userId);

4. 工厂方法模式

过时实现

代码语言:javascript
代码运行次数:0
运行
复制
public class WidgetFactory : IWidgetFactory  // 简单场景的过度设计
{
    public Widget Create() => new Widget();
}

现代替代

代码语言:javascript
代码运行次数:0
运行
复制
// 依赖注入容器自动构造
builder.Services.AddTransient<Widget>();

// 需额外参数时
var widget = ActivatorUtilities.CreateInstance<Widget>(provider, arg1, arg2);

5. 横切关注点装饰器

传统做法

代码语言:javascript
代码运行次数:0
运行
复制
public class CachingRepository : IRepository<T>  // 业务逻辑污染
{
    private readonly IRepository<T> _inner;
    public async Task<T> Get(int id)
    {
        // 缓存逻辑与业务耦合
        return await _inner.Get(id);
    }
}

现代方案

代码语言:javascript
代码运行次数:0
运行
复制
// 中间件统一处理
app.UseResponseCaching();

app.MapGet("/products", async (MyContext db) => 
    await db.Products.ToListAsync())
    .CacheResponse();  // 声明式缓存

6. 适配器模式

过时实现

代码语言:javascript
代码运行次数:0
运行
复制
public class LegacyAdapter : INewApi  // 冗余包装类
{
    private readonly LegacyService _legacy;
    public Data Get() => Convert(_legacy.Fetch());
}

现代替代

代码语言:javascript
代码运行次数:0
运行
复制
public static class LegacyExtensions  // 扩展方法直接适配
{
    public static Data ToNewData(this LegacyService svc) =>
        new Data(svc.Fetch().Value);
}

// 调用:legacyService.ToNewData()

7. 手动单例模式

传统写法

代码语言:javascript
代码运行次数:0
运行
复制
public sealed class Logger  // 线程安全手工实现
{
    private static readonly Lazy<Logger> _instance = new(...);
    public static Logger Instance => _instance.Value;
}

现代方案

代码语言:javascript
代码运行次数:0
运行
复制
// 容器托管生命周期
builder.Services.AddSingleton<Logger>();  // 自动处理线程安全

8. 手工 DTO 映射

过时做法

代码语言:javascript
代码运行次数:0
运行
复制
public static OrderDto Map(Order order) =>  // 手工赋值
    new OrderDto { Id = order.Id, Total = order.Total };

现代方案

代码语言:javascript
代码运行次数:0
运行
复制
// C#记录类型简化
public record OrderDto(int Id, decimal Total);

// 一行完成映射
var dto = new OrderDto(order.Id, order.Total);

9. 共享引用与深层命名空间

传统风格

代码语言:javascript
代码运行次数:0
运行
复制
namespace MyApp.Features.Submodule.Core;  // 冗长嵌套

现代优化

代码语言:javascript
代码运行次数:0
运行
复制
// GlobalUsings.cs
global using System.Text.Json;
global using Microsoft.Extensions.Logging;

// 文件顶部简化
namespace MyApp.Features;  // 文件作用域命名空间

过时模式与现代替代对照表

🚫 过时模式

✅ .NET 10 替代方案

仓储层+工作单元

直接使用 DbContext

服务定位器

构造函数/Minimal API 注入

日志包装器

源生成器 [LoggerMessage]

工厂模式

DI 容器 / ActivatorUtilities

装饰器

中间件/端点过滤器

适配器

扩展方法

手动单例

AddSingleton() 注册

手工 DTO 映射

C# 记录类型

深层命名空间

文件作用域命名空间


重构行动指南

放手依赖多年的模式或许不适——但随着 .NET 进化,这些模式常成为冗余脚手架。

三步重构法: 1️⃣ 从小处开始

  • • 将单个仓储替换为直接 DbContext 调用
  • • 用源生成日志替换一个包装器

2️⃣ 转移横切逻辑

  • • 将装饰器功能迁移到中间件层

3️⃣ 体验收益

  • • 代码可读性提升 40%+
  • • 变更速度提高 2 倍
  • • 调试时间减少 60%
代码语言:javascript
代码运行次数:0
运行
复制
点击下方卡片关注DotNet NB
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 仓储层 + 工作单元
  • 2. 服务定位器
  • 3. 日志包装器
  • 4. 工厂方法模式
  • 5. 横切关注点装饰器
  • 6. 适配器模式
  • 7. 手动单例模式
  • 8. 手工 DTO 映射
  • 9. 共享引用与深层命名空间
  • 过时模式与现代替代对照表
  • 重构行动指南
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档