c++ OOP 设计 - 将数据成员传递给其他类 - 是否合理

c++ OOP design - passing data member to other classes - is it reasonable?

本文关键字:其他 是否 设计 OOP 数据成员 c++      更新时间:2023-10-16

我还有一个问题,希望能让我的想法变得夏天。

假设我有以下 3 个类:

类玩家:

class Player {
private:
    int positionX, positionY;
public:
    void move(Board& b) {
      // player changes its position on the board(move)
      b.removeCharFromBoard(positionX, positionY);
      positionX++;
      positionY++;
      // 'P' indicates a Player in the Board....
      b.insertCharToBoard(positionX, positionY, 'P');
    }
};

班级董事会:

class Board {
private:
    // BOARD_C and BOARD_R are both "#define ..." for some integer number.
    char board[BOARD_C][BOARD_R];
};

类游戏引擎:

class GameEngine {
private:
     Board* board;
public:
     void playTurn(const Player& p) {
        p.move(board);
     }
};

在您看来,GameBoard的playTurn函数将使用参数"board"调用玩家的移动函数是否合理?我需要这样做才能在棋盘数据成员中标记玩家已更改其位置。它是否保留了 OOP 基本规则?

谢谢大家,辛迪加!

是的,在这种情况下,这似乎是合理的("在这种情况下"的意思是"考虑我能猜到的关于你的GameEngineBoard类的语义以及它们的关联/聚合关系的性质"):

  1. 而是使用智能指针而不是原始指针来保存Board对象GameEngine。 在这种情况下,unique_ptr可能是您想要的,因为所有其他别名似乎都只是观察者,并且电路板对象的生存期绑定到GameEngine对象之一。但是,如果需要共享所有权,请选择 shared_ptr .尽量不要使用原始指针、newdelete ,因为它们会导致错误代码;
  2. 您仍然需要在 Board 类的接口上提供公共函数来修改开发板,因为Player将无法访问其私有成员变量(board恰好是一个)。
  3. 不要#define秒,而是使用constexpr值表示电路板的大小(如果您使用的是 C++11)。您可能还需要考虑使用 Boost.MultiArray 来创建安全的二维 C 样式数组。

你的方法很好。游戏引擎将用作游戏的某种控制器。多亏了它,例如,您可以过滤玩家移动,检查这种移动是否可行,或者在特定的玩家操作的情况下做其他类型的事情。

其次,对于

此解决方案,您不必将玩家连接到特定的棋盘,这扩展了您其他选项的可能性,例如在棋盘之间轻松转移玩家。我认为你走得很好:)

您必须考虑您的应用程序将如何更改,以及您希望引入哪些功能。从这段代码来看,它看起来不错,但是当您引入新功能时,它会看起来像这样吗?

另一种解决方案是将仅移动逻辑放入玩家,它将更新其位置,然后您的游戏引擎将根据所有玩家当前位置更新棋盘条目。想象一下,一段时间后您将需要实现碰撞检测,然后在每个玩家更新其位置或动作后,将发生碰撞检测并纠正这些动作,并且只有在您的棋盘上才会正确更新。

很多事情已经说了,但如果我可以补充一些东西。

Board(无论是否private)传递给Player本身并不坏,并且这种设计用于多种体系结构(代码取自SFML 2.0):

void Sprite::draw(RenderTarget& target, RenderStates states) const
{
    if (m_texture)
    {
        states.transform *= getTransform();
        states.texture = m_texture;
        target.draw(m_vertices, 4, Quads, states);
    }
}

RenderTarget是你的Board.这里要了解的是,您将仅使用其公共接口(您有权访问)Board进行操作。在上面的代码中,draw()是在target上用于强制其绘制某些内容的方法。

将内部对象传递给更高级别类(如Player)的整个想法可以解释为桥接OO模式,其中接口Board可以有多个实现,并且多个类可以实现IBoardManipulator(或类似的东西)。

也就是说,我想说的是,遵循游戏引擎的总体思路要好得多,即:

  1. 注册游戏实体(在本例中为玩家)
  2. 捕获玩家输入
  3. 摘要并对播放器输入做出反应(请求移动播放器)
  4. 处理游戏逻辑(检查玩家是否可以移动到给定位置,如果是 - 移动他)
  5. 对于每个已注册的实体,GameEngine将调用draw()传递Board作为实体可以使用的目标。
  6. 从步骤 2 开始重复

我并不是说对于一个简单的架构来说这是必需的,但从长远来看,管理起来比处理每个类似Player类可以对Board做的令人讨厌的事情要容易得多。

相关文章: