解耦的艺术:构建可插拔的文件处理流水线
在软件开发中,我们经常遇到需要按顺序处理多个步骤的场景(如文件处理、审批流)。如果使用传统的硬编码,一旦逻辑变更,我们就得修改核心代码,这违背了“开闭原则”。
为了解决这个问题,我决定引入责任链模式(Chain of Responsibility)。它能将复杂的处理流程拆解为独立的处理器,配合现代 C# 的 Fluent API 风格,我们可以像搭积木一样构建业务流程,实现高度的解耦与灵活配置。
先设计一个上下文(Context)类: 作为“快递盒”,在各个处理器之间传递数据。
namespace ChainOfResponsibilityDemo.Models
{
public class FileContext
{
public string? Folder { get; set; }
public List<string>? Files { get; set; }
public List<string>? Folders { get; set; }
public FileContext()
{
Folders = [];
Files = [];
}
}
}
抽象处理器(BaseHandler): 定义标准接口,持有下一个处理器的引用。
namespace ChainOfResponsibilityDemo.Models
{
public abstract class BaseHandler
{
// 持有下一个处理器的引用
public BaseHandler? Next { get; set; }
/// <summary>
/// 处理请求的入口
/// </summary>
/// <param name="context"></param>
/// <returns>返回当前处理器,支持链式调用</returns>
public virtual BaseHandler Handle(FileContext? context)
{
// 1. 执行当前处理器的逻辑
Handling(context);
// 2. 如果有下一个处理器,传递下去
return Next?.Handle(context) ?? this;
}
/// <summary>
/// 子类必须实现的具体处理逻辑
/// </summary>
/// <param name="context"></param>
public virtual void Handling(FileContext? context)
{
// 默认什么都不做
// 子类可以选择重写,也可以选择不重写
}
}
}
链头入口(HeadChain): 这是本文的亮点,实现了 With 方法来构建流式调用。
namespace ChainOfResponsibilityDemo.Models
{
public class HeadChain : BaseHandler
{
public HeadChain With(BaseHandler handler)
{
ArgumentNullException.ThrowIfNull(handler);
if (Next is null)
{
Next = handler;
}
else
{
// 找到链尾
var current = Next;
while (current.Next is not null)
{
current = current.Next;
}
current.Next = handler;
}
return this;
}
}
}
现在开始设计第一个处理器子类,负责搜索文件。
namespace ChainOfResponsibilityDemo.Models
{
public class GetFilesHandler : BaseHandler
{
public override void Handling(FileContext? context)
{
ArgumentNullException.ThrowIfNull(context);
context.Files = [.. Directory.GetFiles(context.Folder ?? "")];
}
}
}
设计第二个处理器子类,负责搜索文件夹。
namespace ChainOfResponsibilityDemo.Models
{
internal class GetFoldersHandler : BaseHandler
{
public override void Handling(FileContext? context)
{
ArgumentNullException.ThrowIfNull(context);
context.Folders = [.. Directory.GetDirectories(context.Folder ?? "")];
}
}
}
第三个处理器子类,负责遍历文件夹下所有的文件。
namespace ChainOfResponsibilityDemo.Models
{
public class ProcessFoldersHandler : BaseHandler
{
public override void Handling(FileContext? context)
{
ArgumentNullException.ThrowIfNull(context);
var folders = context.Folders?.ToList();
Queue<string> foldersQueue = new(folders ?? []);
while (foldersQueue.Count > 0)
{
string currentFolder = foldersQueue.Dequeue();
foreach (var item in Directory.GetFiles(currentFolder))
{
context?.Files?.Add(item);
}
foreach (var subFolder in Directory.GetDirectories(currentFolder))
{
foldersQueue.Enqueue(subFolder);
}
}
}
}
}
最后一个处理器子类,用于显示最终结果。
namespace ChainOfResponsibilityDemo.Models
{
public class DisplayFilesHandler : BaseHandler
{
public override void Handling(FileContext? context)
{
ArgumentNullException.ThrowIfNull(context);
Console.WriteLine("显示文件");
foreach (var item in context.Files ?? [])
{
Console.WriteLine(item);
}
Console.WriteLine("显示文件夹");
foreach (var item in context.Folders ?? [])
{
Console.WriteLine(item);
}
}
}
}
这种设计模式的核心价值在于‘分离’。
我们将流程的定义(在 Main 方法中通过 With 组装)与流程的执行(在 Handle 中遍历)完全分离。
当未来我们需要增加一个‘文件压缩’步骤,或者删除‘显示文件’步骤时,我们不需要修改一行核心逻辑代码,只需要在 With 链中增删处理器即可。这就是架构的灵活性!
static void Main(string[] args)
{
var fileContext = new FileContext() { Folder = @"D:\BaiduNetdiskDownload" };
var chain = new HeadChain()
.With(new GetFilesHandler())
.With(new GetFoldersHandler())
.With(new ProcessFoldersHandler())
.With(new DisplayFilesHandler());
chain.Handle(fileContext);
Console.ReadLine();
}

浙公网安备 33010602011771号