有两个彼此需要的类是不好的设计吗?
我正在写一个小游戏,其中有一个GameEngine
包含一些GameState
对象的类。要访问几种渲染方法,这些GameState
对象还需要了解GameEngine
类-因此是循环依赖项。
您会称这个设计不好吗?我只是问,因为我不太确定,目前我仍然可以重构这些东西。
有两个彼此需要的类是不好的设计吗?
我正在写一个小游戏,其中有一个GameEngine
包含一些GameState
对象的类。要访问几种渲染方法,这些GameState
对象还需要了解GameEngine
类-因此是循环依赖项。
您会称这个设计不好吗?我只是问,因为我不太确定,目前我仍然可以重构这些东西。
Answers:
从本质上来说,这并不是一个糟糕的设计,但它很容易失控。实际上,您正在日常代码中使用该设计。例如,vector知道它是第一个迭代器,并且迭代器具有指向其容器的指针。
现在,根据您的情况,最好为GameEnigne和GameState使用两个单独的类。既然这两个基本上是在做不同的事情,以后您可能会定义很多继承GameState的类(例如游戏中每个场景的GameState)。没有人可以否认他们需要彼此访问。GameEngine基本上是在运行游戏状态,因此应该有一个指向它们的指针。GameState正在使用GameEngine中定义的资源(例如渲染的,物理管理器等)。
您不能也不应将这两个类彼此合并,因为它们天生就在做不同的事情,而合并将导致一个很大的类,没人喜欢。
到目前为止,我们知道在输出设计中需要循环依赖。有多种安全创建方法:
要得出他的答案,您可以使用这三种方法中的任何一种,但是您需要注意不要过度使用它们中的任何一种,因为正如我所说的那样,所有这些设计都非常危险,并且很容易导致代码无法维护。
有两个彼此需要的类是不好的设计吗?
这有点代码气味,但是可以保留。如果那是让您的游戏启动并运行的更简便快捷的方法,那就去做吧。但是请记住这一点,因为很有可能您必须在某个时候对其进行重构。
C ++的问题在于,循环依赖项不会那么容易地进行编译,因此摆脱它们而不是花时间修复您的编译可能是一个更好的主意。
您会称我的设计不好吗?
不,这总比将所有课程都上一堂更好。
它不是很好,但是实际上与我见过的大多数实现都非常接近。通常,您会有一个用于游戏状态的管理器类(当心!)和一个渲染器类,并且它们是单例是很常见的。因此,循环依赖是“隐藏的”,但潜在的存在。
另外,正如您在评论中告诉您的那样,游戏状态类执行某种渲染有点奇怪。它们应该只保存状态信息,并且渲染应由渲染器或游戏对象本身的某些图形组件处理。
现在可能会有最终的设计。我很好奇,看看其他答案是否能带来一个好主意。不过,您可能还是可以为您的游戏找到最佳设计的人。
通常必须要有两个直接相互引用的类,这是不好的设计。实际上,在代码中跟踪控制流可能会更加困难,对象的所有权及其生存期可能会很复杂,这意味着没有一个类,任何一个类都无法重用,这可能意味着控制流应该真正存在于两个类之外这些类中的第三类是“调解员”类,依此类推。
但是,两个对象相互引用是很常见的,这里的区别是通常一个方向上的关系更加抽象。例如,在Model-View-Controller系统中,View对象可以持有对Model对象的引用,并且将能够了解其所有信息,能够访问其所有方法和属性,以便View可以使用相关数据填充自身。Model对象可以保留对View的引用,以便在其自己的数据发生更改时可以使View更新。但不是模型具有View引用-它将使模型依赖于View-通常,View实现了一个Observable接口,通常只有1个Update()
函数,并且模型包含对Observable对象的引用,该对象可以是View。当模型更改时,它将调用Update()
其所有Observable,而View将Update()
通过回调模型并检索任何更新的信息来实现。这样做的好处是,模型完全不了解视图(为什么要这么做),并且可以在其他情况下重用,即使没有视图也是如此。
您的游戏中也有类似情况。GameEngine通常会知道GameStates。但是GameState不需要了解GameEngine的全部知识,它只需要访问GameEngine上的某些渲染方法即可。这应该在您的脑海中引起一个小警报,该警报是:(a)GameEngine试图在一个类中做太多事情,和/或(b)GameState不需要整个游戏引擎,只需要可渲染部分。
解决此问题的三种方法包括: