对于两个需要相互交互的不同对象编写代码的正确方法是什么?

问题描述 投票:0回答:1

我正在开发一个基于 STM32 的设备项目,该设备连接了多个外设。对于我的问题的最简单版本,这些外围设备是一个:

  • 连接到驱动器的步进电机,由STM32的GPIO引脚驱动
  • 通过UART与STM32通信的触摸屏显示器

显示屏用于显示和操作步进器运行的设置,如速度、减速特性(无/渐变)、反向距离等。

到目前为止,我已经用 C 编写了所有代码,但是当我想扩展外设时(例如:需要另一个步进电机实例),它会变得一团糟。到目前为止,我已经使用易失性全局变量来维护步进器的所有设置,并且一切正常,但我觉得最好为步进电机提供一个对象并维护它用于跟踪其当前状态的所有变量参数作为对象变量。因此,我开始将代码重写为 C++,并为步进电机编写一个类,到目前为止进展顺利,但现在我遇到了问题。

我的程序的当前结构:

  • main.cpp:这里所有对象都被实例化,定时器和UART被初始化,以及FreeRTOS的东西
  • display.hpp:显示对象的头文件
  • display.cpp:显示对象代码
  • stepper.hpp:步进电机对象的头文件
  • stepper.cpp:步进电机代码

显示器使用并操作存储在步进器对象中的数据。以前,当我使用 C 时,我可以简单地将 stepper.h 文件包含在 display.h 文件中,反之亦然,并且它们可以互相调用函数,例如:

显示.c:

int display_process_integer_data(uint8_t *buf) // buf = message stream received from display over UART

    int retval = 0;
    switch (buf[5]) // buf[5] defines the type of data is received from the display, i.e. start/stop, change speed etc. 
    {
    case 0x00: // Start/stop
        if (buf[8] == 0) { stepper_stop(); break; }

        if (buf[8] == 1) { stepper_start(); break; }

    case 0x01: // Change speed
        if ( stepper_set_speed( buf[7]*256 + buf[8] ) ) { retval = -1; break; }
        break;

    default: // No valid case
        retval = -1;
        break;
    }
    return retval;

理想情况下我现在将其更改为:

显示.cpp:

int Display::process_integer_data(uint8_t *buf)

    int retval = 0;
    switch (buf[5]) 
    {
    case 0x00: // Start/stop
        if (buf[8] == 0) { motor1.start(); break; }

        if (buf[8] == 1) { motor1.stop(); break; }

    case 0x01: // Change speed
        if ( motor1.setSpeed( buf[7]*256 + buf[8] ) ) { retval = -1; break; }
        break;

    default: // No valid case
        retval = -1;
        break;
    }
    return retval;

但这不可能。现在步进器是 main.cpp 中的实例化对象,我无法再调用此函数,因为此对象不存在于该范围内。

现在通常我会说“让函数将数据返回给 main.cpp,然后在那里完成其余的工作”,但是这个函数所做的信息比一个返回值所能容纳的信息要多:该函数都包含数字out 接收到的指令类型(存储在 buf[5] 中)以及该指令中包含的数据(作为 int16 存储在 buf[7] 和 buf[8] 中)。所以这是不可能的(我认为?)。

在其他情况下,我通过将对象设置为 Display 类中的变量来解决此问题,例如:

显示.hpp:

class Display
{
protected:
    Stepper __motor = Stepper(uint8_t instantiating_var);
};

但我认为这更像是一种黑客行为,而不是一个正确的解决方案,而且只有当显示器仅与 __motor 交互时它才会起作用。我还需要 Stepper 对象来调用 Display 对象中的函数,因此这是行不通的。 喜欢:

步进器.cpp:

int Stepper::init()
{
    while (0 != display.inform_speed(__speed)) {}

    while (0 != display.inform_startstop(__running_FLAG)) {}

    while (0 != display.inform_slowdown_mode(__mode)) {}
    __initialized_FLAG = 1;

    return 0;
}

(上面可能不是最好的例子,但我可以想象步进电机检测到的警报状态,例如遇到终点停止,需要报告给显示器。我还没有对此进行编程)

所以我的问题最终是:让 main.cpp 中的两个对象访问其他对象方法的正确方法是什么?我觉得我缺少一个非常明显的解决方案来解决这个问题,但我无法理解它。

c++ oop stm32f4
1个回答
0
投票

有一种古老的面向对象模式,称为“模型-视图-控制器”。 它通常专门用于设计交互式应用程序,但其核心思想在其他上下文中作为维护关注点分离的模型很有帮助。 要将其应用到您的项目中:

电机对象应该只知道步进电机。
  • 显示对象应该只知道有关显示的信息。
  • 第三个对象称为控制器,负责处理协调。
  • 在 M-V-C 的理想实现中,运动对象不应该知道显示对象,反之亦然。 双方都不应该互相呼唤。 控制器调用两者,封装应用程序的业务逻辑。 如果电机代码提供反馈,则会发送到控制器,控制器决定如何以及何时在显示屏上呈现该信息。

通常,控制器“拥有”其他对象。 因此,要向项目中添加第二个电机,您需要让控制器实例化电机对象的另一个实例,并且管理它的任何额外工作只涉及对控制器代码的更改。

© www.soinside.com 2019 - 2024. All rights reserved.