解耦的艺术:构建可插拔的文件处理流水线

在软件开发中,我们经常遇到需要按顺序处理多个步骤的场景(如文件处理、审批流)。如果使用传统的硬编码,一旦逻辑变更,我们就得修改核心代码,这违背了“开闭原则”。
为了解决这个问题,我决定引入责任链模式(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();
 }
posted @ 2026-02-21 00:41  孤独的小苗  阅读(8)  评论(0)    收藏  举报