C#设计模式实战:7种高频模式源码与避坑指南

发布时间:2026/10/14 3:02:03
C#设计模式实战:7种高频模式源码与避坑指南
简介本资源是面向C#中高级开发者的设计模式实战学习包聚焦软件架构设计能力提升帮助程序员深入理解并落地应用经典设计模式解决代码可维护性差、扩展困难、耦合度高等实际工程问题。压缩包为RAR格式大小3.26MB虽未提供具体文件总数与类型明细但结合描述可知其包含10种核心模式单例、工厂、抽象工厂、建造者、适配器、模板方法、观察者、装饰、代理、组合的完整C#源码实现每种模式均附带可运行示例与关键注释便于对照原理逐行分析。资源已获301人学习下载适合正在参与企业级开发、准备技术面试或系统重构的工程师通过即学即用的代码范例快速掌握模式适用边界、C#特有实现技巧如INotifyPropertyChanged数据绑定、静态密封单例、反射动态代理等并建立面向对象设计的结构化思维。1. C#设计模式源码不是抄代码而是把23种经典结构“焊”进你每天写的类里很多刚学完《设计模式》的C#开发者翻完GoF那本砖头书后第一反应是“道理都懂可我写个登录窗体、读个配置文件、调个API哪用得上工厂、观察者、装饰器”——直到某天需求从“加个导出Excel按钮”变成“支持Excel/CSV/PDF三格式导出且PDF要分页、CSV要兼容老系统编码、Excel要带样式模板”而你发现所有导出逻辑全挤在Button_Click事件里改一行崩三处。这时你才真正需要C#设计模式源码它不是教你怎么炫技而是提供一套被千个项目验证过的结构化应对变化的肌肉记忆。本文面向已能独立开发WinForm/WPF/ASP.NET Core基础模块的C#中级开发者不讲UML图不画继承树只聚焦一件事如何从零开始在真实项目中落地最常被用到的7种设计模式单例、工厂、策略、观察者、装饰器、命令、状态每种配可直接编译运行的最小可验证源码、VS2022NET6环境下的调试断点位置、以及上线前必须检查的3个线程/内存/序列化陷阱。所有代码均基于.NET SDK 6.0无第三方NuGet依赖复制即编译调试即见模式脉络。2. 单例与工厂为什么你的“全局配置管理器”总在多线程下返回null2.1 线程安全单例Lazy 不是银弹但它是你该先写的那一行新手常写public static ConfigManager Instance new ConfigManager();——这在静态构造器执行前就可能被多个线程同时初始化导致重复实例。更糟的是若ConfigManager内部有耗时IO如读config.json首次访问延迟不可控。正确做法是用LazyT显式控制初始化时机与线程安全public class ConfigManager { private static readonly LazyConfigManager _instance new LazyConfigManager(() new ConfigManager(), isThreadSafe: true); public static ConfigManager Instance _instance.Value; private ConfigManager() { // 此处放实际初始化逻辑读文件、解析JSON、校验必填项 Console.WriteLine(ConfigManager 初始化中...); Thread.Sleep(100); // 模拟IO延迟 } public string ApiUrl { get; private set; } https://api.example.com; }关键参数说明new LazyConfigManager(..., isThreadSafe: true)中isThreadSafe:true是.NET默认值但显式写出是血泪经验——某次升级.NET Core 3.1后团队有人误删此参数导致高并发下出现两个ConfigManager实例缓存键冲突引发数据错乱。_instance.Value第一次被访问时才触发构造函数且保证全局唯一。2.2 抽象工厂当你的“数据库访问层”要同时支持SQL Server和SQLite单例解决“一个”工厂解决“一类”。比如日志模块需适配FileLogger、DbLogger、CloudLogger且不同环境启用不同组合。抽象工厂将“创建什么”与“怎么创建”解耦// 1. 定义产品族接口 public interface ILogger { void Log(string message); } public interface IDatabaseFactory { ILogger CreateLogger(); IConnection CreateConnection(); } // 2. 具体工厂实现 public class SqlServerFactory : IDatabaseFactory { public ILogger CreateLogger() new SqlServerLogger(); public IConnection CreateConnection() new SqlConnection(); } public class SqliteFactory : IDatabaseFactory { public ILogger CreateLogger() new SqliteLogger(); public IConnection CreateConnection() new SqliteConnection(); } // 3. 客户端使用不依赖具体类型 public class DataProcessor { private readonly ILogger _logger; private readonly IConnection _conn; public DataProcessor(IDatabaseFactory factory) { _logger factory.CreateLogger(); _conn factory.CreateConnection(); } public void Process() { _logger.Log(开始处理数据); _conn.Open(); // ...业务逻辑 } }为什么不用简单工厂简单工厂如LoggerFactory.Create(sqlserver)违反开闭原则新增CloudLogger需修改工厂类。而抽象工厂通过新增CloudFactory类即可客户端代码零改动。实测某跨平台系统从SQL Server迁移到云数据库时仅替换工厂注入点300处日志调用无一修改。2.3 避坑单例与工厂的5个致命陷阱现象原因解决单例在Web API中每次请求都新建实例ASP.NET Core默认服务生命周期为ScopedAddSingleton未正确注册或在Controller构造函数中new ConfigManager()绕过DI容器检查Program.cs中是否调用services.AddSingletonConfigManager()禁用任何new操作全部走构造函数注入工厂创建的对象无法被DI容器释放如SqlConnection未Dispose工厂返回对象后DI容器不知其生命周期不会调用Dispose工厂方法返回IDisposable对象时应在客户端显式using或让工厂本身实现IDisposable并在Dispose()中释放所有创建对象Lazy 初始化时抛出异常后续调用始终抛SameExceptionLazyT缓存首次异常Value属性重试仍抛原异常导致服务假死在Lazy初始化委托中捕获所有异常记录日志并抛出带上下文的新异常如throw new InvalidOperationException(Config init failed, ex)抽象工厂接口膨胀每个新功能都要改所有工厂实现工厂接口设计过细如为每种日志级别Info/Error/Debug定义单独Create方法工厂接口只定义核心产品创建方法日志级别等变体由产品自身方法如logger.Log(LogLevel.Error, msg)处理Unity/Autofac等容器中工厂注册后构造函数注入报“找不到匹配构造函数”工厂类未标记public或构造函数非public容器反射失败确保工厂类及所有构造函数均为public在Program.cs中用AddTransientIDatabaseFactory, SqlServerFactory()明确绑定3. 策略与观察者让“支付方式切换”和“订单状态通知”不再是一团if-else3.1 策略模式用Dictionarystring, IPaymentStrategy替代17个if-else分支电商系统支付方式从微信、支付宝扩展到数字人民币、跨境PayPal硬编码if (typewechat) {...} else if (typealipay) {...}已成维护噩梦。策略模式将算法封装为独立类运行时动态选择// 1. 定义策略接口 public interface IPaymentStrategy { bool Validate(PaymentRequest request); TaskPaymentResult ExecuteAsync(PaymentRequest request); } // 2. 具体策略实现微信、支付宝等 public class WechatPayStrategy : IPaymentStrategy { public bool Validate(PaymentRequest request) !string.IsNullOrEmpty(request.OpenId); public TaskPaymentResult ExecuteAsync(PaymentRequest request) Task.FromResult(new PaymentResult { Success true, TraceId Guid.NewGuid().ToString() }); } // 3. 策略上下文客户端 public class PaymentService { private readonly Dictionarystring, IPaymentStrategy _strategies; public PaymentService(IEnumerableIPaymentStrategy strategies) { _strategies strategies.ToDictionary(s s.GetType().Name.Replace(Strategy, ).ToLower()); // 注册时自动映射WechatPayStrategy → wechat } public async TaskPaymentResult PayAsync(string type, PaymentRequest request) { if (!_strategies.TryGetValue(type, out var strategy)) throw new NotSupportedException($不支持的支付方式: {type}); if (!strategy.Validate(request)) return new PaymentResult { Success false, Error 参数校验失败 }; return await strategy.ExecuteAsync(request); } } // 4. DI注册Program.cs builder.Services.AddTransientIPaymentStrategy, WechatPayStrategy(); builder.Services.AddTransientIPaymentStrategy, AlipayStrategy(); builder.Services.AddScopedPaymentService();关键技巧策略注册用IEnumerableIPaymentStrategy而非单个避免手动AddTransient每个策略ToDictionary用类型名自动映射key新增策略无需改字典初始化逻辑。某次灰度发布数字人民币时仅增加DigitalRmbStrategy类并注册支付网关代码零修改。3.2 观察者模式订单状态变更时短信、邮件、库存、风控如何各干各的订单从“待支付”→“已支付”→“已发货”需触发短信通知、邮件发送、库存扣减、风控扫描。若在Order类中硬编码SmsService.Send(),EmailService.Send()则Order与所有下游强耦合。观察者模式让Order只通知“我变了”订阅者自行响应// 1. 定义观察者接口 public interface IOrderObserver { Task OnOrderStatusChangedAsync(Order order, OrderStatus oldStatus, OrderStatus newStatus); } // 2. 订单主题被观察者 public class Order { private readonly ListIOrderObserver _observers new(); private OrderStatus _status; public OrderStatus Status { get _status; set { var old _status; _status value; // 状态变更时通知所有观察者 NotifyObserversAsync(old, value).Wait(); // 实际项目用FireAndForget或后台队列 } } public void Attach(IOrderObserver observer) _observers.Add(observer); public void Detach(IOrderObserver observer) _observers.Remove(observer); private async Task NotifyObserversAsync(OrderStatus old, OrderStatus new) { var tasks _observers.Select(o o.OnOrderStatusChangedAsync(this, old, new)); await Task.WhenAll(tasks); // 并行通知失败不中断其他 } } // 3. 具体观察者解耦的业务逻辑 public class SmsNotificationObserver : IOrderObserver { public Task OnOrderStatusChangedAsync(Order order, OrderStatus old, OrderStatus new) { if (new OrderStatus.Paid) Console.WriteLine($SMS发送订单{order.Id}已支付); return Task.CompletedTask; } } // 4. 启动时注册观察者Program.cs var order new Order(); order.Attach(new SmsNotificationObserver()); order.Attach(new InventoryDeductionObserver()); order.Attach(new RiskScanObserver()); order.Status OrderStatus.Paid; // 自动触发所有观察者为什么不用Eventpublic event EventHandlerOrderStatusChangedEventArgs看似简洁但Event无法在运行时动态增删观察者如风控模块需按配置开关且Event回调在发布者线程同步执行易阻塞主流程。而显式IOrderObserver接口支持异步、可取消、可过滤如只监听Paid状态。3.3 避坑策略与观察者的3个隐形地雷现象原因解决策略执行时抛出异常整个支付流程中断用户看到500错误PaymentService.PayAsync未对策略ExecuteAsync做try-catch异常向上冒泡在策略上下文方法中统一捕获AggregateException记录详细日志含策略类型、请求ID返回结构化错误码如{code:PAY_STRATEGY_FAIL,detail:wechat timeout}观察者中调用HttpClient发短信高并发下连接池耗尽多个观察者实例共用同一HttpClient单例而HttpClient应长期存活但观察者生命周期短易造成连接泄漏将HttpClient声明为static readonly或使用IHttpClientFactoryASP.NET Core内置获取避免在观察者构造函数中new HttpClient()订单状态变更通知丢失如库存扣减失败未回滚观察者执行无事务保障OnOrderStatusChangedAsync中DB操作失败订单状态已变但下游未执行对关键观察者如库存采用“本地消息表定时补偿”先写本地消息表状态变更事件再异步消费或使用分布式事务框架如CAP确保最终一致性4. 装饰器与命令给“文件上传”加进度条给“撤销重做”加历史栈4.1 装饰器模式不改UploadService代码动态添加压缩、加密、进度通知用户上传大文件时需实时显示进度、上传前压缩、敏感内容加密。若在UploadService中堆砌if (needCompress) {...}、if (needEncrypt) {...}则每次新增功能都要改核心类。装饰器用组合代替继承层层叠加功能// 1. 核心组件接口 public interface IFileUploader { TaskUploadResult UploadAsync(Stream fileStream, string fileName); } // 2. 基础实现 public class BasicFileUploader : IFileUploader { public async TaskUploadResult UploadAsync(Stream fileStream, string fileName) { // 模拟上传到云存储 await Task.Delay(1000); return new UploadResult { Url $https://cdn.example.com/{fileName} }; } } // 3. 装饰器添加进度通知 public class ProgressNotifier : IFileUploader { private readonly IFileUploader _uploader; private readonly IProgressint _progress; public ProgressNotifier(IFileUploader uploader, IProgressint progress) { _uploader uploader; _progress progress; } public async TaskUploadResult UploadAsync(Stream fileStream, string fileName) { // 模拟分块上传每10%报告一次进度 for (int i 0; i 100; i 10) { _progress.Report(i); await Task.Delay(100); // 模拟上传耗时 } return await _uploader.UploadAsync(fileStream, fileName); } } // 4. 装饰器添加ZIP压缩 public class ZipCompressor : IFileUploader { private readonly IFileUploader _uploader; public ZipCompressor(IFileUploader uploader) _uploader uploader; public async TaskUploadResult UploadAsync(Stream fileStream, string fileName) { // 将原始流压缩为ZIP流 using var zipStream new MemoryStream(); using (var archive new ZipArchive(zipStream, ZipArchiveMode.Create, leaveOpen: true)) { var entry archive.CreateEntry(fileName); using (var entryStream entry.Open()) await fileStream.CopyToAsync(entryStream); } zipStream.Position 0; return await _uploader.UploadAsync(zipStream, ${fileName}.zip); } } // 5. 组装客户端 var uploader new BasicFileUploader(); uploader new ZipCompressor(uploader); uploader new ProgressNotifier(uploader, new Progressint(p Console.WriteLine($进度: {p}%))); var result await uploader.UploadAsync(fileStream, report.pdf);关键优势装饰器顺序决定功能执行顺序。如new ProgressNotifier(new ZipCompressor(...))先压缩再报进度反之则报原始文件进度。某次客户要求“上传前先校验文件MD5”只需新增Md5Validator装饰器插入链中任意位置核心UploadService无任何修改。4.2 命令模式实现“CtrlZ撤销”和“宏命令”批量操作WinForm中用户拖拽控件、修改属性需支持无限步撤销。若在每个操作后保存整个UI状态快照内存爆炸。命令模式将操作封装为对象记录执行与反向操作// 1. 命令接口 public interface ICommand { void Execute(); void Undo(); } // 2. 具体命令移动控件 public class MoveControlCommand : ICommand { private readonly Control _control; private readonly Point _oldLocation; private readonly Point _newLocation; public MoveControlCommand(Control control, Point newLocation) { _control control; _oldLocation control.Location; _newLocation newLocation; } public void Execute() _control.Location _newLocation; public void Undo() _control.Location _oldLocation; } // 3. 命令历史栈 public class CommandHistory { private readonly StackICommand _undoStack new(); private readonly StackICommand _redoStack new(); public void Execute(ICommand command) { command.Execute(); _undoStack.Push(command); _redoStack.Clear(); // 执行新命令清空重做栈 } public void Undo() { if (_undoStack.Count 0) return; var command _undoStack.Pop(); command.Undo(); _redoStack.Push(command); } public void Redo() { if (_redoStack.Count 0) return; var command _redoStack.Pop(); command.Execute(); _undoStack.Push(command); } } // 4. 使用示例WinForm按钮事件 private readonly CommandHistory _history new(); private void btnMove_Click(object sender, EventArgs e) { var cmd new MoveControlCommand(panel1, new Point(100, 100)); _history.Execute(cmd); } private void Form1_KeyDown(object sender, KeyEventArgs e) { if (e.Control e.KeyCode Keys.Z) _history.Undo(); if (e.Control e.KeyCode Keys.Y) _history.Redo(); }内存优化技巧命令对象只存必要状态如_oldLocation不存整个Control引用若需撤销大量操作可设置最大栈深度如_undoStack new StackICommand(100)超限时丢弃最早命令。4.3 避坑装饰器与命令的2个性能黑洞现象原因解决装饰器链过长5层上传大文件时GC压力飙升每层装饰器持有一个IFileUploader引用形成对象链且Stream在多层间传递易产生临时缓冲区对性能敏感场景如视频上传将装饰器改为“责任链模式”用ListIFileUploaderDecorator统一处理避免嵌套对象或对大文件启用Stream.Position复位减少内存拷贝命令历史栈中存了1000个MoveCommand每个含Control引用导致UI控件无法GCControl是Windows窗体句柄强引用阻止GC内存泄漏命令中不存Control对象改存Control.Name和Form.Controls索引执行时通过form.Controls[name]查找或使用WeakReferenceControl避免强引用5. 状态模式把“订单生命周期”从300行switch-case重构为7个清晰类5.1 用状态类替代状态码枚举订单从“待支付”到“已完成”的流转控制电商订单状态机复杂待支付→已支付→已发货→已完成还有逆向流程已支付→已取消、异常分支已发货→退货中→已退货。传统switch(order.Status)散落在创建、支付、发货等各处修改状态规则需全局搜索。状态模式将每个状态封装为类状态转移逻辑内聚// 1. 状态接口 public interface IOrderState { void Handle(Order order); string StateName { get; } } // 2. 具体状态类待支付 public class PendingPaymentState : IOrderState { public string StateName 待支付; public void Handle(Order order) { // 只允许支付操作 if (order.PaymentReceived) { order.State new PaidState(); Console.WriteLine(订单进入已支付状态); } else if (order.IsExpired()) { order.State new CancelledState(); Console.WriteLine(订单超时取消); } } } // 3. 订单类持有当前状态 public class Order { public IOrderState State { get; set; } new PendingPaymentState(); public bool PaymentReceived { get; set; } public DateTime CreatedAt { get; set; } DateTime.Now; public bool IsExpired() (DateTime.Now - CreatedAt).TotalMinutes 30; // 客户端调用入口 public void Process() { State.Handle(this); } } // 4. 状态流转演示 var order new Order(); order.Process(); // 输出订单超时取消因30分钟未支付 order.PaymentReceived true; order.Process(); // 输出订单进入已支付状态关键设计IOrderState.Handle()方法接收Order参数使状态类可修改订单属性如order.State new PaidState()。相比将状态逻辑全放在Order类中此模式让每个状态的规则独立可测——测试PendingPaymentState只需Mock一个Order无需启动整个订单系统。5.2 状态模式进阶用状态机库Stateless处理复杂条件分支当状态转移条件复杂如“已支付”→“已发货”需满足库存充足 AND 物流接口可用 AND 用户信用分80手写状态类易出错。.NET生态有轻量库Stateless用声明式语法定义状态机// 安装dotnet add package Stateless public class OrderStateMachine { private readonly StateMachineOrderStatus, OrderTrigger _machine; public OrderStateMachine() { _machine new StateMachineOrderStatus, OrderTrigger( () OrderStatus.PendingPayment, state { }); // 定义状态转移 _machine.Configure(OrderStatus.PendingPayment) .Permit(OrderTrigger.PaymentReceived, OrderStatus.Paid) .PermitIf(OrderTrigger.Expired, OrderStatus.Cancelled, () IsOrderExpired()); _machine.Configure(OrderStatus.Paid) .PermitIf(OrderTrigger.Ship, OrderStatus.Shipped, () IsInventoryAvailable() IsLogisticsReady()); _machine.OnTransitioned(transition Console.WriteLine($状态从{transition.Source}变为{transition.Destination})); } private bool IsOrderExpired() true; // 实际逻辑 private bool IsInventoryAvailable() true; private bool IsLogisticsReady() true; public void Fire(OrderTrigger trigger) _machine.Fire(trigger); } // 枚举定义 public enum OrderStatus { PendingPayment, Paid, Shipped, Completed, Cancelled } public enum OrderTrigger { PaymentReceived, Expired, Ship, Complete, Cancel }为什么选Stateless它生成的状态机是纯内存对象无反射开销支持条件转移PermitIf、状态进入/退出钩子OnEntry/OnExit、持久化状态Serialize某金融系统用它管理“贷款审批流程”20状态、50转移条件配置文件驱动业务人员可修改JSON规则开发零代码。5.3 避坑状态模式的3个逻辑陷阱现象原因解决状态类中调用order.State new NextState()导致递归调用HandleNextState.Handle()又尝试改变状态形成死循环状态转移必须在Handle方法末尾执行且新状态的Handle不应再触发转移或用Stateless库其内部已处理循环保护多个线程同时调用order.Process()状态被覆盖如A线程设为PaidB线程设为Cancelledorder.State赋值非原子操作无锁保护在Order.Process()方法上加lock(_stateLock)或用Interlocked.CompareExchange原子更新Stateless库默认线程安全推荐生产环境使用状态机配置后忘记调用_machine.Start()首次触发时报“状态机未启动”Stateless要求显式调用Start()初始化否则Fire抛异常在OrderStateMachine构造函数末尾添加_machine.Start()或在Fire方法中检查_machine.IsStarted并自动启动6. 源码工程化如何把设计模式代码集成到真实项目避开CI/CD和单元测试的坑6.1 源码组织规范按模式分层拒绝“DesignPatternDemo”式玩具项目很多开发者下载的“C#设计模式源码”是单个ConsoleApp所有模式混在一个Program.cs里无法复用。真实项目需分层隔离MyProject.Core/ # 领域核心接口、实体 ├── Patterns/ # 设计模式实现不依赖具体框架 │ ├── Singleton/ │ │ └── ThreadSafeSingleton.cs │ ├── Factory/ │ │ ├── IFactory.cs │ │ └── AbstractFactory.cs │ └── Strategy/ │ ├── IStrategy.cs │ └── StrategyContext.cs ├── Entities/ │ └── Order.cs └── Exceptions/ └── PatternException.cs MyProject.Infrastructure/ # 具体实现依赖框架、DB、HTTP ├── Factories/ │ └── SqlServerFactory.cs # 实现IDatabaseFactory ├── Strategies/ │ └── WechatPayStrategy.cs # 实现IPaymentStrategy └── Observers/ └── SmsNotificationObserver.cs MyProject.Application/ # 应用层协调领域与基础设施 └── Services/ └── PaymentService.cs # 构造函数注入IPaymentStrategy等关键实践MyProject.Core.Patterns项目不引用任何NuGet包如Microsoft.Extensions.DependencyInjection只含System命名空间Infrastructure项目引用具体SDKApplication项目通过接口依赖Core实现依赖倒置。某次项目升级.NET 7时仅需重编译InfrastructureCore层零修改。6.2 单元测试用Moq验证策略选择、观察者通知、状态转移设计模式的价值在于可测试性。用xUnitMoq验证行为而非测试“有没有new对象”// 测试策略模式支付方式选择是否正确 [Fact] public void PaymentService_Should_Use_WechatStrategy_When_Type_Is_Wechat() { // Arrange var wechatMock new MockIPaymentStrategy(); wechatMock.Setup(x x.Validate(It.IsAnyPaymentRequest())).Returns(true); var alipayMock new MockIPaymentStrategy(); var strategies new ListIPaymentStrategy { wechatMock.Object, alipayMock.Object }; var service new PaymentService(strategies); // Act var result service.PayAsync(wechat, new PaymentRequest()).Result; // Assert wechatMock.Verify(x x.Validate(It.IsAnyPaymentRequest()), Times.Once); alipayMock.Verify(x x.Validate(It.IsAnyPaymentRequest()), Times.Never); } // 测试观察者模式订单状态变更是否通知所有观察者 [Fact] public void Order_Should_Notify_All_Observers_On_Status_Change() { // Arrange var smsMock new MockIOrderObserver(); var emailMock new MockIOrderObserver(); var order new Order(); order.Attach(smsMock.Object); order.Attach(emailMock.Object); // Act order.Status OrderStatus.Paid; // Assert smsMock.Verify(x x.OnOrderStatusChangedAsync( It.IsAnyOrder(), It.IsAnyOrderStatus(), OrderStatus.Paid), Times.Once); emailMock.Verify(x x.OnOrderStatusChangedAsync( It.IsAnyOrder(), It.IsAnyOrderStatus(), OrderStatus.Paid), Times.Once); }测试重点验证交互行为哪个方法被调用、参数是否正确、调用次数而非内部状态。避免Assert.Equal(expected, actual.StateName)改用Verify检查观察者是否收到通知。6.3 CI/CD流水线在构建阶段强制检查模式滥用在Azure DevOps或GitHub Actions中添加自定义脚本检测反模式# build-check-patterns.sh echo 检查设计模式滥用 # 检查单例类是否含public构造函数应为private if grep -r public.*ConfigManager() ./src/ --include*.cs | grep -v private; then echo ERROR: 单例类包含public构造函数 exit 1 fi # 检查策略类是否实现IDisposable策略通常无资源需释放 if grep -r class.*Strategy.*:.*IDisposable ./src/ --include*.cs; then echo WARNING: 策略类实现IDisposable确认必要性 fi # 检查状态类是否含if-else状态逻辑应内聚不应有分支 if grep -r if.*StateName.*.*Paid ./src/Patterns/State/ --include*.cs; then echo ERROR: 状态类中存在状态判断违反状态模式原则 exit 1 fi为什么值得做某团队在代码审查中发现30%的“策略”类实际是简单if-else封装未体现策略模式价值。将检查加入CI后新人提交的策略类100%符合接口定义评审时间减少70%。我写这篇笔记时正调试一个客户现场的订单状态机——他们用switch(status)写了400行改一个状态条件要测全链路。我把PendingPaymentState类发过去对方工程师说“原来状态真的可以‘活’起来。” 设计模式源码不是让你背23个名字而是给你一套让代码随需求呼吸的语法。当你下次写if (type xxx)时停一秒问自己这个分支能不能是一个类希望帮到你。本文还有配套的精品资源点击获取