C# 策略模式在 WinForm 工业软件中的实战应用

0 阅读6分钟

前言

还在为复杂的业务逻辑切换而头疼吗?面对不断变化的需求,是否感觉代码越来越臃肿,维护成本越来越高?

作为一名长期深耕工业软件领域的 C# 开发,深知这种痛苦。一个生产系统今天要支持高速模式,明天又要加入节能策略,后天客户又提出新的质量优先流程——如果用传统的 if-else 或 switch-case 硬编码,不出三个月,核心模块就会变成"意大利面条"。

今天,不讲教科书定义,不堆砌 UML 图,而是通过一个真实可运行的工业控制系统案例,手把手带你用 策略模式 打造一个生产级 WinForm 应用。

你会看到:

  • 如何设计清晰的策略接口

  • 如何实现具体业务策略(高速/标准/节能)

  • 如何与 UI 完美集成

  • 那些踩过的坑和避坑指南

最终你将拥有一套可复用、可测试、可扩展的架构模板。

为什么需要策略模式?

传统开发的痛点

想象一个工业产线控制系统,需支持三种典型生产模式:

  • 高速生产模式:紧急订单,追求最高产出效率

  • 标准生产模式:日常作业,平衡效率与能耗

  • 节能生产模式:非高峰时段,优先降低功耗

若采用传统写法,代码很快会变成这样:

// ❌ 糟糕的实现方式
public void StartProduction(string mode, int quantity)
{
    if (mode == "HighSpeed")
    {
        // 高速模式逻辑...
    }
    else if (mode == "Standard")
    {
        // 标准模式逻辑...
    }
    else if (mode == "Eco")
    {
        // 节能模式逻辑...
    }
    // 新增模式就要修改这里...
}

问题显而易见

  • 违反开闭原则:每次新增模式都要动核心方法

  • 高耦合:业务逻辑与控制流混杂,难以单元测试

  • 可读性差:随着策略增多,方法迅速膨胀,新人看不懂、老手不敢改

这不仅是代码问题,更是交付风险

设计流程

策略模式解决方案

核心设计思想

将可变的算法族封装成独立类,使它们可以互相替换,且不影响使用它们的客户端。

关键在于:解耦"做什么"和"怎么做"

架构设计

首先定义统一策略接口:

// ✅ 策略接口定义
public interface IProductionStrategy
{
    string StrategyName { get; }
    ProductionResult Execute(int quantity, double workTime);
    string GetDescription();
    double GetEfficiency();
    double GetPowerConsumption();
}

再定义数据模型,用于承载执行结果:

public class ProductionResult
{
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; }
    public int TargetQuantity { get; set; }
    public int ActualQuantity { get; set; }
    public double Efficiency { get; set; }
    public double PowerConsumption { get; set; }
    public string Status { get; set; }
    public string StrategyUsed { get; set; }
    public double WorkTime { get; set; }
    
    public TimeSpan Duration => EndTime - StartTime;
    public double QualityRate => (double)ActualQuantity / TargetQuantity * 100;
}

项目效果

代码实战:具体策略实现

高速生产策略

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AppStrategyIndustrialControlSystem.Interfaces;
using AppStrategyIndustrialControlSystem.Models;

namespace AppStrategyIndustrialControlSystem.Strategies
{
    public class HighSpeedStrategy : IProductionStrategy
    {
        public string StrategyName => "高速生产模式";
        
        public ProductionResult Execute(int quantity, double workTime)
        {
            var result = new ProductionResult
            {
                StartTime = DateTime.Now,
                TargetQuantity = quantity,
                WorkTime = workTime,
                StrategyUsed = StrategyName
            };
            
            // 高速模式:效率高,但功耗大
            double efficiency = GetEfficiency();
            double powerConsumption = GetPowerConsumption();
            
            // 模拟生产过程
            System.Threading.Thread.Sleep(1000); // 模拟处理时间
            
            result.ActualQuantity = (int)(quantity * efficiency);
            result.Efficiency = efficiency;
            result.PowerConsumption = powerConsumption * workTime;
            result.EndTime = DateTime.Now;
            result.Status = result.ActualQuantity >= quantity * 0.95 ? "生产完成" : "需要调整";
            
            return result;
        }
        
        public string GetDescription()
        {
            return "适用于紧急订单,最大化生产速度,功耗较高";
        }
        
        public double GetEfficiency()
        {
            return 0.98; // 98% 效率
        }
        
        public double GetPowerConsumption()
        {
            return 150.0; // 150kW/小时
        }
    }
}

类似地,StandardStrategyEcoModeStrategy 只需实现相同接口,返回不同的效率、功耗和描述。

上下文管理器

策略不能孤立存在,需要一个"调度中心":

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AppStrategyIndustrialControlSystem.Interfaces;
using AppStrategyIndustrialControlSystem.Models;

namespace AppStrategyIndustrialControlSystem.Context
{
    public class ProductionContext
    {
        private IProductionStrategy _strategy;
        
        public ProductionContext(IProductionStrategy strategy)
        {
            _strategy = strategy;
        }
        
        public void SetStrategy(IProductionStrategy strategy)
        {
            _strategy = strategy;
        }
        
        public ProductionResult ExecuteProduction(int quantity, double workTime)
        {
            return _strategy?.Execute(quantity, workTime);
        }
        
        public IProductionStrategy GetCurrentStrategy()
        {
            return _strategy;
        }
    }
}

上下文持有当前策略引用,并提供统一调用入口。

UI 设计:工业级界面实现

控件命名规范

遵循工业软件惯例:

  • Button → btn

  • ComboBox → cmb

  • DataGridView → dgv

  • NumericUpDown → nud

核心界面代码

初始化顺序至关重要:

private void InitializeData()
{
    // ⚠️ 重要:正确的初始化顺序
    InitializeStrategies();      // 1. 策略列表
    InitializeContext();         // 2. 上下文
    InitializeDataGridView();    // 3. 数据网格
    SetDefaultStrategy();        // 4. 默认选项
}

private void cmbStrategy_SelectedIndexChanged(object sender, EventArgs e)
{
    // 🛡️ 防御性编程
    if (cmbStrategy.SelectedItem is IProductionStrategy selectedStrategy && 
        _productionContext != null)
    {
        _productionContext.SetStrategy(selectedStrategy);
        UpdateStrategyInfo(selectedStrategy);
    }
}

常见问题提醒

NullReferenceException 陷阱

问题:在 Form 构造函数中填充 ComboBox 时,会触发 SelectedIndexChanged 事件,但此时 _productionContext 尚未初始化,导致空引用异常。

解决方案:临时移除事件处理器。

// ✅ 临时移除事件处理器
cmbStrategy.SelectedIndexChanged -= cmbStrategy_SelectedIndexChanged;
// 初始化操作...
cmbStrategy.SelectedIndexChanged += cmbStrategy_SelectedIndexChanged;

Readonly 字段赋值问题

若将策略列表声明为 readonly,则无法在 InitializeStrategies() 中赋值:

// ❌ 错误
private readonly List<IProductionStrategy> _strategies;

// ✅ 正确
private List<IProductionStrategy> _strategies;

异步操作最佳实践

避免 UI 冻结,必须异步执行耗时操作:

private async void btnExecute_Click(object sender, EventArgs e)
{
    btnExecute.Enabled = false;
    pgbProgress.Visible = true;
    
    try
    {
        // 🚀 异步执行,避免UI冻结
        var result = await Task.Run(() => 
            _productionContext.ExecuteProduction(quantity, workTime));
            
        // 更新UI...
    }
    catch (Exception ex)
    {
        MessageBox.Show($"生产过程发生错误:{ex.Message}", "错误");
    }
    finally
    {
        btnExecute.Enabled = true;
        pgbProgress.Visible = false;
    }
}

实际应用场景

策略模式远不止用于生产模式切换:

  • 工业控制系统:设备参数动态调整、报警阈值策略

  • 金融交易系统:多套风控规则、止盈止损算法

  • 游戏开发:AI 行为树、难度自适应逻辑

  • 电商系统:运费计算、折扣策略、会员等级服务

只要存在"同一问题多种解法",策略模式就是首选。

策略模式的核心优势

  • 高度灵活:运行时动态切换算法,无需重启或重编译

  • 易于扩展:新增策略只需实现接口,零侵入现有代码

  • 便于测试:每个策略可独立 Mock 和验证

  • 代码整洁:彻底消除 if-else 膨胀,提升可读性与可维护性

进阶建议

工厂模式结合

public static class StrategyFactory
{
    public static IProductionStrategy CreateStrategy(string strategyType)
    {
        return strategyType switch
        {
            "HighSpeed" => new HighSpeedStrategy(),
            "Standard" => new StandardStrategy(),
            "Eco" => new EcoModeStrategy(),
            _ => throw new ArgumentException("未知策略类型")
        };
    }
}

配置化管理

将策略参数外置到 appsettings.json

{
  "ProductionStrategies": {
    "HighSpeed": {
      "Efficiency": 0.98,
      "PowerConsumption": 150.0
    }
  }
}

策略缓存

避免重复创建实例:

private static readonly Dictionary<string, IProductionStrategy> _strategyCache 
    = new Dictionary<string, IProductionStrategy>();

数据持久化

public interface IDataRepository
{
    Task SaveProductionResultAsync(ProductionResult result);
    Task<List<ProductionResult>> GetHistoryAsync(DateTime from, DateTime to);
}

实时监控

public event EventHandler<ProductionEventArgs> ProductionStatusChanged;

private void NotifyProductionStatus(ProductionResult result)
{
    ProductionStatusChanged?.Invoke(this, new ProductionEventArgs(result));
}

总结

通过这个完整的工业控制系统案例,我们验证了策略模式在真实项目中的强大价值:

1、架构清晰:接口抽象 + 具体实现,职责分明

2、扩展无忧:新增生产模式?只需加一个类,不碰一行旧代码

3、工程可靠:从异常处理到异步调用,覆盖生产环境所有细节

策略模式不是炫技,而是应对变化的工程智慧。它教会我们:好的架构不是预测未来,而是让未来的变化成本最低

当你下次面对一堆 if-else 时,不妨问自己一句:"这些分支,是不是该交给策略来管了?"

关键词

C#、策略模式、设计模式、WinForm、工业控制、IProductionStrategy、ProductionContext、策略接口、开闭原则、代码重构、异步执行、防御性编程、工厂模式、配置化管理

mp.weixin.qq.com/s/AOfq9oq5Oo_1uraAzWDmrA

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!