当前位置: 首页 > news >正文

用 Command 模式构建可扩展的命令行工具

用 Command 模式构建可扩展的 C# 命令行工具(支持多命令与路径解析)

在开发工具型程序(如:数据转换、图像处理、批处理工具)时,一个常见的演进过程是:

一个 Main → 一堆 if-else → 越来越难维护

本文介绍一种工程实践中非常成熟的做法:
用 Command 模式重构命令行工具,让每个功能成为一个独立命令(Command),主程序只负责调度(dispatch)。


一、问题背景:为什么要重构 CLI 结构?

传统写法通常是这样:

static void Main(string[] args)
{
    if (args[0] == "a") { /* 功能 A */ }
    else if (args[0] == "b") { /* 功能 B */ }
    else if (args[0] == "c") { /* 功能 C */ }
}

当功能增加后,会出现:

  • • Main 过于臃肿
  • • 功能之间强耦合
  • • 不利于测试与扩展
  • • 不利于长期维护

更合理的目标是:

新增一个命令,只新增一个文件
主程序不需要修改逻辑


二、设计目标

我们希望命令行工具具备以下特性:

  • • 每个功能是一个独立命令
  • • 支持命令行传参(如 -i input -o output
  • • 支持相对路径 / 绝对路径切换
  • • 主程序只负责「分发命令」

 

 

 

 

CommandXXX
CommandContext
Args
BaseDirectory
PathResolver

三、整体架构概览

CLI Tool
│
├── Program.cs          // 只做 dispatch
├── ICommand.cs         // Command 抽象
├── CommandContext.cs   // 参数 & 路径上下文
├── PathResolver.cs    // 路径解析
│
├── Commands/
│   ├── CommandA.cs
│   ├── CommandB.cs
│   └── CommandC.cs

四、核心思想:Command 模式

1️⃣ Command 接口

public interface ICommand
{
    string Name { get; }
    string Description { get; }
    void Execute(CommandContext context);
}
  • • Name:命令名(如 convertexport
  • • Execute:命令执行入口

2️⃣ 示例 Command(功能模块)

public class CommandExample : ICommand
{
    public string Name => "example";
    public string Description => "Run example task";    public void Execute(CommandContext ctx)
    {
        string input  = ctx.ResolvePath("i");
        string output = ctx.ResolvePathOrDefault("o", "out.dat");        ExampleProcessor.Run(input, output);
    }
}

👉 每个命令一个类,职责单一


五、主程序:只负责 Dispatch

class Program
{
    static readonly List<ICommand> Commands = new()
    {
        new CommandExample(),
        new CommandOther()
    };    static void Main(string[] args)
    {
        var (cmdName, options) = ArgParser.Parse(args);        var command = Commands.FirstOrDefault(c => c.Name == cmdName);
        if (command == null)
        {
            PrintHelp();
            return;
        }        var context = new CommandContext(options);
        command.Execute(context);
    }
}

主程序的特点:

  • • 不包含业务逻辑
  • • 不关心参数含义
  • • 只负责:
    • • 找到 Command
    • • 调用 Execute

六、命令行参数解析(示例)

public static class ArgParser
{
    public static (string, Dictionary<string, string>) Parse(string[] args)
    {
        string command = args[0];
        var dict = new Dictionary<string, string>();        for (int i = 1; i < args.Length - 1; i++)
        {
            if (args[i].StartsWith("-"))
                dict[args[i].TrimStart('-')] = args[++i];
        }        return (command, dict);
    }
}

示例调用:

tool.exe example -i data/input.json -o result.bin

七、路径处理:支持相对 / 绝对模式

命令行工具中,路径问题非常常见。

CommandContext

public class CommandContext
{
    public Dictionary<string, string> Args { get; }
    public string BaseDir { get; }    public CommandContext(Dictionary<string, string> args)
    {
        Args = args;
        BaseDir = args.ContainsKey("absolute")
            ? Directory.GetCurrentDirectory()
            : AppContext.BaseDirectory;
    }    public string ResolvePath(string key)
    {
        return PathResolver.Resolve(Args[key], BaseDir);
    }
}

PathResolver

public static class PathResolver
{
    public static string Resolve(string path, string baseDir)
    {
        return Path.IsPathRooted(path)
            ? path
            : Path.GetFullPath(Path.Combine(baseDir, path));
    }
}

支持:

tool.exe example -i data/a.json
tool.exe example -i data/a.json --absolute

八、用到了哪些设计模式?

✅ Command Pattern(核心)

  • • 每个命令封装一个操作
  • • 主程序通过接口统一调用

✅ Strategy Pattern(弱形式)

  • • 不同 Command = 不同执行策略
  • • 运行时选择

✅ Context Object(工程实践)

  • • 参数、路径、环境信息集中管理
  • • Command 不直接依赖全局状态

九、这种结构适合什么场景?

非常适合:

  • • 数据处理工具
  • • 验证工具
  • • Unity / OpenCV / Web 辅助工具
  • • 内部工程 CLI 工具链

甚至可以无缝接入 Unity Editor 或 CI 流水线


十、总结

通过 Command 模式重构命令行工具,可以获得:

  • • 清晰的结构
  • • 易扩展、易维护
  • • 新功能零侵入
  • • 工程级可读性
http://www.jsqmd.com/news/279629/

相关文章:

  • 详细介绍:Django与前端框架集成:Vue.js、React的完美配合
  • 告别笨重 Jenkins,试试轻量级开源 CI/CD:Arbess
  • 2026年北京美国本科留学机构服务机构解析:美国藤校申请 /美国本科申请 /美本活动规划 /美国留学招生官/美国本科前30深度解析
  • 从功能到性价比,这款国产PCB设计软件值得推荐
  • Day19简单和引用数据类型
  • max-num-batched-tokens计算方法
  • 2026年1月钛管/钛棒/钛锻件厂家Top5推荐:实力企业精选,覆盖多场景钛材需求
  • 丽水市莲都青田缙云遂昌松阳区英语雅思培训辅导机构推荐,2026权威出国雅思课程中心学校口碑排行榜推荐
  • 2025年汽车托运行业口碑标杆企业,汽车托运推荐榜单技术实力与市场口碑领航者
  • 为什么顶级团队都在用混合分段策略?,解密Dify高精度检索背后的秘密
  • 2026年1月陕西省青少年戒网瘾学校推荐:三大头部机构专业解析
  • QBrush之材质画刷(Qt::BrushStyle::TexturePattern)
  • 飞书审批表单动态渲染 + Dify LLM意图识别 = 全自动审批路由(已落地金融客户,RPA替代率提升63%)
  • 拓客 ROI 翻倍秘诀:实测 5 款工具,千元预算也能精准获客
  • 当电路设计遇上“安全可控”需求,推荐这款国产PCB设计软件
  • 昌吉回族昌吉阜康呼图壁玛纳斯奇台吉木萨尔木垒哈萨克英语雅思辅导机构推荐,2026权威出国雅思课程中心学校口碑排行榜
  • verl如何保证训练稳定性?容错机制部署解析
  • AI(学习笔记第十五课)从langchain的v0.3到v1.0 - 指南
  • 2026年隧道代理购买参考:10家主流机构实测与选型指南
  • Qwen3-Embedding-0.6B显存占用高?量化压缩部署教程
  • 从测试到上线:dify高可用集群部署全流程详解(含拓扑图与配置清单)
  • 伊犁哈萨克伊宁奎屯霍城巩留英语雅思辅导机构推荐,2026权威出国雅思课程中心学校口碑排行榜
  • 揭秘pip安装失败真相:如何快速修复“Externally-managed-environment“错误?
  • 高校实验室智能化升级:RFID技术革新化学试剂管理
  • 手写报销单识别准确率低怎么办
  • 想成为Java架构师需要掌握什么内容?
  • 掌握这7条语法规则,轻松玩转Dify提示词中的变量替换
  • 【Python装饰器避坑权威指南】:20年资深工程师亲授类方法中装饰器的5大致命陷阱及修复方案
  • FSMN-VAD部署必装哪些库?Python与系统依赖清单详解
  • Z-Image-Turbo免费吗?开源模型部署教程及合规使用指南