策略模式用好关键在于接口只声明行为契约、Context仅持并调用策略、DI管理生命周期。IStrategy应单一方法、无实现细节;Context禁new具体策略;命名需业务语义化,职责须严格隔离。

策略模式在 C# 里不是靠“学完教程”就能用好的,关键在于接口定义是否隔离了变化点、上下文是否真的只依赖抽象——多数人写崩是因为把具体策略的创建逻辑塞进了 Context 类里。
如何定义 IStrategy 接口才不踩坑
接口不能暴露具体实现细节,比如不要包含 ConnectionString 或 ApiKey 这类配置字段;它只该声明行为契约。常见错误是把策略当成配置容器,结果每次加个新策略就得改接口,违背开闭原则。
- 只保留一个核心方法,如
Execute()或带明确语义的名称(CalculateDiscount()、ValidateInput()) - 参数尽量用 DTO 或基础类型,避免传
HttpContext、IServiceProvider等框架强依赖对象 - 返回值统一用
Result<T>或自定义状态类,别混用bool+out string
Context 类最容易写错的三件事
Context 不是策略工厂,也不是策略缓存器,它的唯一职责是持有并调用当前策略。很多人在这里偷偷耦合了创建逻辑或条件判断,导致单元测试无法注入不同策略。
- 构造函数只接收
IStrategy,不接收Type、string name或配置节对象 - 禁止在 Context 内部 new 具体策略(如
new SqlPaymentStrategy()),这会让策略不可替换 - 如果需要运行时切换策略,用属性或方法注入(
SetStrategy(IStrategy s)),而不是在 Execute 里 if-else 拿配置决定用哪个
.NET 6+ 中用 DI 替代手动 new 策略实例
硬编码 new 策略不仅难测,还会让生命周期失控(比如本该 Scoped 的策略被当 Singleton 用)。交给 DI 容器管理是最自然的解法。
注册方式示例:
services.AddScoped<IStrategy, EmailNotificationStrategy>();
services.AddScoped<IStrategy, SmsNotificationStrategy>();
// 或用 Keyed Services(.NET 8+)
services.AddKeyedScoped<IStrategy>("email", sp => new EmailNotificationStrategy());
services.AddKeyedScoped<IStrategy>("sms", sp => new SmsNotificationStrategy());使用时直接从 IServiceProvider 解析,或通过工厂封装:
- 用
IServiceProvider.GetRequiredService<IStrategy>()适合简单场景 - 需要多策略共存时,推荐工厂接口
IStrategyFactory,内部根据 key 返回对应实例 - 注意 Scoped 策略在后台服务中可能提前释放,得用
IServiceScopeFactory创建新 scope
实战中策略命名和边界常被忽略
真实项目里,策略类名往往暴露了技术选型(RedisCacheStrategy),但业务方真正关心的是“什么时候用它”。命名应体现意图而非实现,否则换掉 Redis 改用 MemoryCache 时,你就得重命名所有引用点。
- 用业务语义命名:把
FileExportStrategy改成HighVolumeDataExportStrategy,把ApiRateLimitStrategy改成PartnerTierRateLimitStrategy - 每个策略类只做一件事,不要在
CalculateTax()里顺手发日志或调第三方 API——那是装饰器或管道的事 - 策略间禁止互相调用,也不该共享状态字段(静态变量、单例服务中的可变字段)
最麻烦的不是写不出策略模式,而是后期维护时发现某个策略悄悄改了全局配置,或者 Context 被迫承担路由、缓存、重试等本不属于它的责任。


















