如何构建具有多种输入和输出功能的嵌入式系统。一些基于硬件,一些基于软件设置

How to architect an embedded system with multiple input and output capabilities. Some based on hardware, some on software settings

我有一个在 Arduino 框架中编程的 ESP8266 项目,它从网络收集数据,然后显示在显示器上。该设备可以使用几种不同的显示硬件类型(eink、led、oled)构建。这些是在编译时使用#defines 设置的。然而,也有一些不同类型的数据和数据传输机制可以使用。有些需要硬件(LoRa TX/RX)并在编译时启用,但有些可以在运行时根据用户设置(例如 HTTP 或 MQTT)进行更改。

我已经在使用工厂设计模式在运行时实例化数据传输对象,但仍然使用编译时构建标志 select 来显示要使用的硬件。我有一个显示 class、一个数据源 class 和一个配置 class。此方法运行良好,但在我尝试添加蜂窝网络功能时已达到极限。

我想知道是否有一个好的设计模式/架构设计可以促进这种灵活性,而不必在我的代码中不断添加越来越多的侵入性 #ifdef 语句。

附件是这个设备的基本布局的小思维导图。

如果你想决定在运行时应该注入什么算法,那么你可以尝试使用策略模式。

正如 wiki 所说 strategy pattern:

In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use

所以你可以阅读你的配置文件并选择应该实例化的对象。比如你有很多显示器:

public enum DisplayMark
{
    Samsung, Sony, Dell
}

然后你应该创建一个基础 class Display:

public abstract class Display 
{
    public abstract string Show();
}

然后你需要这个基础的具体实现 class Display:

public class SamsungDisplay : Display
{
    public override string Show()
    {
        return "I am Samsung";
    }
}

public abstract class SonyDisplay : Display
{
    public override string Show()
    {
        return "I am Sony";
    }
}

public abstract class DellDisplay : Display
{
    public override string Show()
    {
        return "I am Dell";
    }
}

到目前为止,还不错。现在我们需要像映射器这样的东西,它将负责通过从配置中选择的显示带来正确的实例:

public class DisplayFactory
{
    public Dictionary<DisplayMark, Display> DisplayByMark { get; private set; } 
        = new Dictionary<DisplayMark, Display>
    {
        { DisplayMark.Sony, new SonyDisplay()},
        { DisplayMark.Samsung, new SamsungDisplay()},
        { DisplayMark.Dell, new DellDisplay()},
    };
}

然后当你从配置文件中知道应该使用什么显示器时,你就可以获得所需的实例:

public void UseDisplay(DisplayMark displayMark) 
{
    DisplayFactory displayFactory = new DisplayFactory();
    Display display = displayFactory.DisplayByMark[displayMark];
    // Here you can use your desired display class
    display.Show();
}