【问题标题】:Does Extern Break EncapsulationExtern 是否破坏封装
【发布时间】:2015-03-24 18:49:35
【问题描述】:

我是 C++ 新手,我正在创建一个游戏。我有一个名为 main 的类,我在其中声明

Game * game; //globally
int main() {
    game = new Game();
    game->show();
}

我的类游戏启动我的游戏等。现在在其他类(玩家、敌人等)中,我使用游戏访问变量,例如玩家健康状况

#include<game.h>
extern Game * game;
func::func() {
    game->health->resetHealth();
}

这是打破封装/OOD 范式吗?这是不好的做法吗?问题是我可以看到任何其他的游戏方式。

【问题讨论】:

  • 在您给出的示例中,您可以轻松使用参数。
  • 为什么不让游戏成为main中的局部变量?

标签: c++ c qt oop extern


【解决方案1】:

是的,extern 破坏了封装。封装的主要概念是数据隐藏和绑定单个实体中对象的属性和行为。 制作变量extern 会违反法律。
在更高级的OOP 语言中,例如java,没有extern。在 Java 中,它总是建议将属性/字段设为私有以限制其访问。

【讨论】:

    【解决方案2】:

    我的意思是,是的,它没有被封装。 game 是一个全局指针,可以从任何地方访问和更改。封装是关于数据隐藏,game 完全暴露。它也不是典型的面向对象设计。为了正确封装和 OOD,您应该限制谁使用和“了解”Game * game。例如,您可以拥有一个由Game * 组成的GameController 对象。 Game * 的范围和生命周期可以存在于GameController 中,然后GameController 可以封装其成员变量,将其设为私有并决定访问指针的人员、方式和时间。还有其他方法,例如将指针包装在全局单例类中。这比您的示例更好,因为包装类可以强制执行某些不变量(例如访问游戏时应该发生什么,或者客户端应该如何删除游戏)。通常,由于此答案范围之外的原因,全局单例不是最佳方法。另一种方法是使用依赖注入。因此,每当一个类需要修改Game * 时,都会将指针传递给它。这些都是用于访问封装数据的面向对象技术。

    【讨论】:

    • 单例只是另一个全局。提这个不是个好主意。
    • 您好,感谢您的回复。您能否更好地向我解释一下我将如何使用游戏控制器或使用依赖注入。看到我正在制作一个游戏,我让它运行,但我使用了很多计时器,我无法用不同的方式来做这件事。我来自java背景,正在使用qt来制作它。我不只是想让它工作,如果你知道我的意思,我想把它搞定@JosephMalike 编辑此外,游戏本身没有任何属性,它只是创建我的窗口等。
    • 这是一个相当大的话题。那里有很多很棒的设计资源。也许您可以在 Stack Exchange Programmers 或 Stack Exchange Code Review 上发帖。做一些谷歌搜索“依赖注入”、“面向对象的设计组合”和“面向对象的设计模式”。祝你好运!
    【解决方案3】:

    仅仅拥有一个已经的全局变量就开始破坏你的封装,因为它提供了从程序中的任何代码访问对象的途径。当你有一个像 any 这样的全局函数时,可以产生改变游戏的副作用,甚至是完全不相关的对象实例中的副作用。使用 extern 不会进一步破坏封装,因为它大致相当于将更多代码粘贴到声明全局的单个源文件中。

    【讨论】:

      【解决方案4】:

      使用全局变量本身不会破坏封装——如果它不是全局的,对象可以尽可能多地隐藏其实现细节。然而,在大多数情况下,它确实与另一个设计原则相冲突:灵活性。比如说,您希望您的程序一次处理多个游戏(如果这样的后期更改根本没有意义,您可能会决定将其保持为全局)。

      每当您要声明某个全局变量时,不妨问问自己:

      • 很难避免吗? (在所示情况下,只需将 game 对象传递给需要它的函数即可。)
      • 有没有可能在以后的某个时间点不只有一种这样的对象? (例如,您可能想为游戏的关卡编写一个编辑器,您希望在其中重用一些代码,并且该编辑器应该能够在多个选项卡中编辑多个游戏。)
      • 是否使用全局强化阅读代码? (请记住,易读比易写更重要!)

      在我看来,在大多数情况下,最好避免使用全局变量;我认为对于问题中显示的情况最好避免使用它。 game 参数可以轻松传递。

      【讨论】:

        【解决方案5】:

        简而言之,是的,这打破了封装。您不断引用游戏对象这一事实表明您对该对象具有依赖关系。

        你可能会考虑在你的程序中有一个初始化序列,称为组合根,(对于简单的例子 main 是最明显的地方),你可以通过构造函数,称为依赖注入。

        如果您想更进一步,您可以创建基类并传递抽象基类而不是实现对象。这样您就可以更多地针对接口而不是对象进行编程。

        【讨论】:

          猜你喜欢
          • 2012-11-28
          • 1970-01-01
          • 1970-01-01
          • 2022-11-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-20
          • 2011-05-10
          相关资源
          最近更新 更多