我有一个课程Game
,课程为EnemyManager
。 EnemyManager
处理敌人的产生及其背后的逻辑。问题是EnemyManager
需要访问游戏类中的函数和其他Game
对象。我可以想到两种方法来解决这个问题。
使用Game*
作为this
中的参数之一传递类对象EnemyManager
的地址。
声明一个指向Game对象的全局指针,并在初始化Game
类时设置它。然后将其extern到enemymanager.cpp
。
这是更明智的方法吗?
答案 0 :(得分:4)
每当遇到这样的情况时,我都会检查相关课程的整体设计。如果EnemyManager是Game对象的成员并且需要在Game中调用内容,那么Game中的那些函数可以被分解为一个单独的组件。如果你正在写的东西开始变得过于复杂或者像是黑客,那么通常是时候做一些保理。
答案 1 :(得分:1)
很难说不知道整体架构布局,但我的2美分:
您描述为第一个的方式称为dependency injection,并且被广泛使用。你应该留意你正在公开的方法/领域。
我假设Game
类具有不应该从EnemyManager
类访问的方法,因此看起来创建具有{所使用的方法的声明的接口的接口似乎是个好主意。 {1}}然后将指针传递给EnemyManager
实例(而不是EnemyManager
)。
例如:Game
类实现Game
,并且您使用此作为初始化参数之一传递IGameEnemyManager
。
答案 2 :(得分:1)
在处理面向对象的设计时,通常会考虑谁将如何处理如何找到设计的第一个版本。在编写了这个版本之后,人们经常会发现这些弱点并在第二次迭代时重写它。
因此,在这种情况下,Game
类管理世界(我假设)并提供不同的方法来操纵它。你的EnemyManager
管理世界的一个方面,敌人,但他们确实生活在世界的内部。
class Enemy {
public:
Enemy(Location& location, int hitpoints);
bool Move(Direction& direction);
};
class Game {
public:
bool CreateInitialState();
bool AddEnemy(Enemy& enemy);
bool AddBullet(Location& location, Direction& direction, int speed);
void Render();
};
class EnemyManager {
public:
EnemyManager(Game& game);
void MoveEnemies();
};
在第一个版本中,所有类型都将相互视为正确的类,并通过调用适当的方法来操作事物。如果你想为它添加新东西,这对扩展游戏几乎没有支持。
这就是接口变得方便的地方,您可以尝试考虑不同部分的交互方式,而不是如何实现它们。
class Canvas {
public:
// Different graphical primitives.
};
class GameObject {
public:
virtual ~GameObject() {};
virtual void Draw(Canvas& canvas) = 0;
virtual bool Move(Direction& direction) = 0;
};
class GlobalState {
public:
virtual AddGameObject(GameObject& gameObject) = 0;
};
class Game : public Canvas, public GlobalState {
public:
bool CreateInitialState();
void Render() {
// Send itself to the Draw method in all GameObjects that have been added
}
// Other game logic
};
class Enemy : public GameObject {
// This can be specialized even more if you need to
};
class Bullet : public GameObject {
// This can also be specialized even more if you need to
};
这将设计与实施区分开来,正如我所看到的,这是一种最终尝试正确的好方法。
答案 3 :(得分:0)
如果您在game objects
中处理EnemyManager
,为什么它会成为课程Game
的一部分?我想你应该考虑检查你的设计,因为如果你不能很好地处理这些情景,就有机会出现循环引用问题。
Consider segregating both the classes to ensure a single responsibility principle.
{EnemyManager {1}}
Define proper interface in your
这些是我能想到的关于你的设计的有限想法的小建议
答案 4 :(得分:0)
您绝对需要使用第一种方法,但只需进行一些更改:您应该将Game
类分解为更多组件。例如,您可以创建一个SceneManager
类,它负责所有游戏对象的创建/管理。当您实例化EnemyManager
时 - 只需传递指向它的指针:
// in SceneManager
EnemyManager* emgr = new EnemyManager(this);
InterfaceManager* imgr = new InterfaceManager(this);
请注意,您的SceneManager
类应提供完整的界面
// in EnemyManager
GameObject* spawnEnemyAt(string name, EnemyClass* eclass, Vector3 position, AIBehaviour* behaviour)
{
GameObject* myEnemy = smgr->createGameObject(name, position, etc...);
//register an enemy in the enemies list, initialize it's behaviour and do any other logic
return myEnemy
}
这种方法可以帮助您破坏您的架构,而不是在friend
(类)区域中捕获。
[更新] 请注意,我的方法假设场景中的所有对象都是GameObject
s,既没有Enemy
也没有Player
类。每个GameObject
可能都有Renderer
,Animator
,AIBehaviour
个组件。