C# 策略模式实现方法 C#如何封装可变的算法

来源:这里教程网 时间:2026-02-21 17:42:57 作者:

策略接口定义要聚焦行为契约,别塞状态字段

策略模式的核心是把算法的「变化点」抽象成接口,而不是提前规划所有可能的字段。比如你要封装不同排序逻辑,

ISortStrategy
只该有
Sort(int[] data)
方法,别加
IsStable
MaxDepth
这类配置属性——这些属于具体策略内部实现细节,或应通过构造函数注入。

常见错误是把策略接口设计得像配置类:

ISortStrategy
里塞一堆
get
属性,结果每个实现都返回默认值,徒增维护成本。

接口方法参数尽量精简,必要时用
record
封装输入(如
SortOptions
),但别让接口本身承担配置职责
避免在接口中定义
Init()
Dispose()
等生命周期方法——策略实例应是无状态、可复用的
如果算法需要外部服务(如日志、缓存),通过构造函数传入依赖,而非在接口里暴露
SetLogger(ILogger)
这类 setter

策略注册与解析要用 ServiceProvider,别手写 Dictionary + new

手动维护

Dictionary<string isortstrategy></string>
并用
new QuickSortStrategy()
填充,会带来硬编码、生命周期失控、测试困难三重问题。.NET 6+ 应直接用
IServiceCollection
注册策略族,再通过
IServiceProvider
解析。

例如注册多个排序策略:

services.AddSingleton<ISortStrategy, QuickSortStrategy>();
services.AddSingleton<ISortStrategy, MergeSortStrategy>();
services.AddSingleton<ISortStrategy, HeapSortStrategy>();

然后用

GetServices<isortstrategy>()</isortstrategy>
获取全部,或按名称解析(需配合
KeyedService
):

services.AddKeyedSingleton<ISortStrategy, QuickSortStrategy>("quick");
services.AddKeyedSingleton<ISortStrategy, MergeSortStrategy>("merge");

运行时根据配置字符串选策略,比 if-else new 更易测、更易换。

不要在业务代码里 new 策略实例——这会让单元测试无法 mock 行为 若策略需传参(如超时时间),用工厂委托注册:
services.AddSingleton<isortstrategy>(sp => new QuickSortStrategy(timeout: 5000))</isortstrategy>
注意作用域:无状态策略用
Singleton
;带缓存或上下文的考虑
Scoped
,但要确认线程安全

Context 类只做策略分发,不掺杂算法逻辑

SortContext
这类上下文类,唯一职责是接收输入、选策略、调用
Execute()
,绝不该出现
if (data.Length  这种分支逻辑——那已经是策略选择规则,应抽成独立的 <code>IStrategySelector

典型反例:

public class SortContext
{
    public void Sort(int[] data)
    {
        if (data.Length < 50) // ❌ 把选择逻辑写死在 Context 里
            new InsertionSort().Sort(data);
        else
            new QuickSort().Sort(data);
    }
}

正确做法是让

SortContext
依赖
IStrategySelector<isortstrategy></isortstrategy>
,而选择器本身可基于数据特征、配置、甚至性能采样动态决定。

Context 构造函数只接受策略接口或策略工厂,不接受具体实现类型 避免给 Context 加模板方法(如
BeforeSort()
/
AfterSort()
)——那是装饰器或管道的事,不是策略模式的职责
如果策略执行需统一异常处理或日志,用中间件或包装器(如
LoggingSortStrategy
),别污染 Context

泛型策略基类容易误用,慎用 TStrategy 约束

有人试图用泛型约束统一策略族,比如

class StrategyContext<tstrategy> where TStrategy : class, IStrategy</tstrategy>
,结果发现无法在运行时切换
TStrategy
——泛型参数是编译期确定的。这种写法实际把策略“固化”了,失去动态性。

真正需要泛型的地方,是策略接口本身支持类型参数(如

ISortStrategy<t></t>
),且所有实现都针对同一组泛型参数;或者用泛型工厂方法封装创建逻辑。

别为了“看起来通用”而加泛型,先问:这个类型参数是否真会影响策略行为?是否所有策略实现都共用它? 若只是想让 Context 支持多种策略接口(如
ISortStrategy
ICompressStrategy
),用接口继承或组合,而不是泛型类型参数
泛型策略类(如
SortStrategy<t></t>
)适合做基类,但必须确保子类能被 DI 容器识别并注册,否则会因类型擦除导致解析失败

策略模式真正的复杂点不在语法,而在边界划分:什么算“算法”,什么算“策略选择逻辑”,什么算“上下文环境”。这三个层次一旦混在一起,再多的泛型和接口也救不了。

相关推荐

热文推荐