通过修改注入的实例(例如替换 HardwareService 的实现)来改变 MainForm 的行为,而无需修改 MainForm 的代码

通过修改注入的实例(例如替换 HardwareService 的实现)来改变 MainForm 的行为,而无需修改 MainForm 的代码。这正是依赖注入(Dependency Injection, DI)的核心优势之一:通过更改容器中的服务注册,可以替换依赖的实现,而不影响依赖这些服务的类(如 MainForm)。以下我会详细说明如何在你的 WinForms 示例中实现这一点,并提供具体的修改方法和代码示例。


目标

  • 不修改 MainForm.cs 的代码,仅通过更改 Program.cs 中的服务注册,替换 HardwareService 的实现(例如从真实硬件服务切换到模拟服务)。

  • 保持 MainForm 的逻辑不变,利用依赖注入的解耦特性。


实现思路

  1. 定义接口:为 HardwareService 定义一个接口(例如 IHardwareService),让 MainForm 依赖接口而不是具体实现。

  2. 注册不同实现:在 Program.cs 中,通过修改 ServiceCollection 的注册逻辑,注入不同的 IHardwareService 实现(例如 HardwareService 或 MockHardwareService)。

  3. 保持 MainForm 不变:MainForm 通过构造函数接收 IHardwareService,无需关心具体实现。


具体修改步骤

1. 定义接口

创建一个 IHardwareService 接口,定义 HardwareService 的核心方法。

IHardwareService.cs:

csharp

namespace WinFormDemo.Services
{
    public interface IHardwareService
    {
        string Connect();
        string SendCommand(string command);
        string GetStatus();
        void Disconnect();
    }
}

2. 修改 HardwareService 实现接口

让 HardwareService 实现 IHardwareService 接口(假设已有实现,只需添加接口)。

HardwareService.cs(修改后):

csharp

using System;
using System.IO.Ports;

namespace WinFormDemo.Services
{
    public class HardwareService : IHardwareService
    {
        private readonly HardwareConfig _config;
        private SerialPort _serialPort;
        private bool _isConnected;

        public HardwareService(HardwareConfig config)
        {
            _config = config;
            _isConnected = false;
        }

        public string Connect()
        {
            try
            {
                if (!_config.IsEnabled)
                    return "硬件未启用,请检查配置。";

                _serialPort = new SerialPort(_config.PortName, _config.BaudRate);
                _serialPort.Open();
                _isConnected = true;
                return $"已连接到 {_config.PortName},波特率:{_config.BaudRate}";
            }
            catch (Exception ex)
            {
                _isConnected = false;
                return $"连接失败:{ex.Message}";
            }
        }

        public string SendCommand(string command)
        {
            if (!_isConnected)
                return "未连接到硬件!";

            try
            {
                _serialPort.WriteLine(command);
                string response = $"模拟硬件响应: {command}";
                return response;
            }
            catch (Exception ex)
            {
                return $"命令发送失败:{ex.Message}";
            }
        }

        public string GetStatus()
        {
            return _isConnected ? "已连接" : "未连接";
        }

        public void Disconnect()
        {
            if (_serialPort != null && _serialPort.IsOpen)
            {
                _serialPort.Close();
                _isConnected = false;
            }
        }
    }
}

3. 创建模拟实现

创建一个模拟的 IHardwareService 实现,用于测试或开发环境。

MockHardwareService.cs:

csharp

namespace WinFormDemo.Services
{
    public class MockHardwareService : IHardwareService
    {
        private readonly HardwareConfig _config;
        private bool _isConnected;

        public MockHardwareService(HardwareConfig config)
        {
            _config = config;
            _isConnected = false;
        }

        public string Connect()
        {
            if (!_config.IsEnabled)
                return "硬件未启用,请检查配置。";

            _isConnected = true;
            return $"模拟连接到 {_config.PortName},波特率:{_config.BaudRate}";
        }

        public string SendCommand(string command)
        {
            if (!_isConnected)
                return "未连接到硬件!";

            return $"模拟响应: {command.ToUpper()}";
        }

        public string GetStatus()
        {
            return _isConnected ? "模拟已连接" : "模拟未连接";
        }

        public void Disconnect()
        {
            _isConnected = false;
        }
    }
}

4. 修改 MainForm 使用接口

修改 MainForm 的构造函数,让它依赖 IHardwareService 而不是 HardwareService。

MainForm.cs(仅修改构造函数部分,其余保持不变):

csharp

using System;
using System.Windows.Forms;
using WinFormDemo.Services;

namespace WinFormDemo.Forms
{
    public partial class MainForm : Form
    {
        private readonly IHardwareService _hardwareService; // 修改为接口

        public MainForm(IHardwareService hardwareService) // 修改构造函数参数
        {
            InitializeComponent();
            _hardwareService = hardwareService;
            UpdateStatus();
        }

        // 其余代码保持不变
        private void btnConnect_Click(object sender, EventArgs e)
        {
            string result = _hardwareService.Connect();
            txtOutput.AppendText(result + Environment.NewLine);
            UpdateStatus();
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            _hardwareService.Disconnect();
            txtOutput.AppendText("已断开连接" + Environment.NewLine);
            UpdateStatus();
        }

        private void btnSendCommand_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(txtCommand.Text))
            {
                MessageBox.Show("请输入命令!");
                return;
            }

            string result = _hardwareService.SendCommand(txtCommand.Text);
            txtOutput.AppendText(result + Environment.NewLine);
            txtCommand.Clear();
        }

        private void UpdateStatus()
        {
            lblStatus.Text = $"状态:{_hardwareService.GetStatus()}";
        }

        // InitializeComponent 和控件定义保持不变
    }
}

说明:

  • MainForm 现在依赖于抽象的 IHardwareService 接口,而不是具体的 HardwareService 类。

  • 这样,MainForm 的代码与具体实现解耦,可以注入任何实现 IHardwareService 的类。

5. 修改 Program.cs 切换实现

通过修改 Program.cs 中的服务注册,控制注入的 IHardwareService 实现。

Program.cs(支持切换实现):

csharp

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
using System.Windows.Forms;
using WinFormDemo.Forms;
using WinFormDemo.Services;

namespace WinFormDemo
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            var configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build();

            var services = new ServiceCollection();
            var hardwareConfig = configuration.GetSection("HardwareSettings").Get<HardwareConfig>();

            services.AddSingleton(hardwareConfig);
            // 选择注入的实现
            bool useMock = true; // 切换开关:true 使用模拟服务,false 使用真实服务
            if (useMock)
            {
                services.AddSingleton<IHardwareService, MockHardwareService>();
            }
            else
            {
                services.AddSingleton<IHardwareService, HardwareService>();
            }
            services.AddSingleton<MainForm>();

            var serviceProvider = services.BuildServiceProvider();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(serviceProvider.GetService<MainForm>());
        }
    }
}

说明:

  • 通过 useMock 变量控制注入的实现:

    • useMock = true:注入 MockHardwareService(模拟服务)。

    • useMock = false:注入 HardwareService(真实硬件服务)。

  • 可以在运行时通过环境变量、配置文件或其他逻辑动态设置 useMock。

6. 使用 IOptions 进一步优化(可选)

为了更符合 .NET 最佳实践,可以使用 IOptions<HardwareConfig> 管理配置。

修改 Program.cs:

csharp

services.Configure<HardwareConfig>(configuration.GetSection("HardwareSettings"));
services.AddSingleton<IHardwareService>(sp =>
{
    bool useMock = true; // 动态切换
    var config = sp.GetService<IOptions<HardwareConfig>>().Value;
    return useMock ? new MockHardwareService(config) : new HardwareService(config);
});
services.AddSingleton<MainForm>();

修改 HardwareService 和 MockHardwareService:

csharp

public class HardwareService : IHardwareService
{
    private readonly HardwareConfig _config;

    public HardwareService(IOptions<HardwareConfig> configOptions)
    {
        _config = configOptions.Value;
    }
    // 其余代码不变
}

public class MockHardwareService : IHardwareService
{
    private readonly HardwareConfig _config;

    public MockHardwareService(IOptions<HardwareConfig> configOptions)
    {
        _config = configOptions.Value;
    }
    // 其余代码不变
}

运行效果

  1. 使用 MockHardwareService:

    • 设置 useMock = true。

    • 运行程序,点击“连接”按钮,输出类似“模拟连接到 COM3,波特率:9600”。

    • 发送命令“START”,输出“模拟响应: START”。

  2. 使用 HardwareService:

    • 设置 useMock = false。

    • 运行程序,点击“连接”按钮,输出类似“已连接到 COM3,波特率:9600”。

    • 发送命令“START”,输出“模拟硬件响应: START”。

关键点:

  • MainForm.cs 的代码完全没有变化,所有的切换逻辑都在 Program.cs 的服务注册中完成。

  • 通过注入不同的 IHardwareService 实现,MainForm 的行为随之改变,体现了依赖注入的解耦优势。


为什么这样做有效?

  1. 依赖接口而非实现:

    • MainForm 依赖 IHardwareService 接口,而不是具体的 HardwareService 或 MockHardwareService。

    • 这符合 SOLID 原则中的依赖倒置原则(Depend on abstractions, not concretions)。

  2. 容器解析依赖:

    • ServiceCollection 负责解析 MainForm 的构造函数依赖。

    • 当容器看到 MainForm 需要 IHardwareService 时,会根据注册的实现(HardwareService 或 MockHardwareService)提供对应的实例。

  3. 灵活切换实现:

    • 在 Program.cs 中通过修改 services.AddSingleton<IHardwareService, T>() 或工厂方法,轻松切换注入的实现。

    • 这种方式特别适合开发/测试环境(使用模拟服务)和生产环境(使用真实服务)之间的切换。

如何扩展?

  1. 动态选择实现:

    • 通过配置文件或环境变量控制注入的实现。例如,在 appsettings.json 中添加:

      json

      {
        "HardwareSettings": {
          "PortName": "COM3",
          "BaudRate": 9600,
          "IsEnabled": true,
          "UseMock": true
        }
      }

      修改 Program.cs:

      csharp

      var hardwareConfig = configuration.GetSection("HardwareSettings").Get<HardwareConfig>();
      services.AddSingleton(hardwareConfig);
      if (hardwareConfig.UseMock)
      {
          services.AddSingleton<IHardwareService, MockHardwareService>();
      }
      else
      {
          services.AddSingleton<IHardwareService, HardwareService>();
      }
  2. 添加更多实现:

    • 可以创建其他 IHardwareService 实现(如 BluetoothHardwareService),并在 Program.cs 中注册。

    • 例如:

      csharp

      services.AddSingleton<IHardwareService, BluetoothHardwareService>();
  3. 使用工厂模式:

    • 使用工厂方法动态创建服务实例:

      csharp

      services.AddSingleton<IHardwareService>(sp =>
      {
          var config = sp.GetService<HardwareConfig>();
          string serviceType = configuration["ServiceType"]; // 例如 "Mock", "Hardware", "Bluetooth"
          return serviceType switch
          {
              "Mock" => new MockHardwareService(config),
              "Hardware" => new HardwareService(config),
              _ => throw new NotSupportedException("Unknown service type")
          };
      });

总结

通过以下步骤,你可以在不修改 MainForm 代码的情况下,灵活切换注入的实例:

  1. 定义 IHardwareService 接口,让 MainForm 依赖接口。

  2. 创建多个实现(如 HardwareService 和 MockHardwareService)。

  3. 在 Program.cs 中通过 services.AddSingleton<IHardwareService, T>() 或工厂方法选择注入的实现。

  4. 可选使用 IOptions<HardwareConfig> 优化配置管理。

这种方法利用了依赖注入的解耦特性,使得 MainForm 的行为可以通过容器配置灵活改变,适合开发、测试和生产环境的切换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhxup606

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值