在我的游戏Bitfighter中,我有一个名为AbstractTeam的类,它有两个子类:Team和EditorTeam。他们共享许多方法,但Team跟踪产生点(实现addSpawn()和getSpawns()方法),而EditorTeam根本不关心产生点。
我可以看到实现这个的两种设计:
我可以在Team中实现addSpawn()和getSpawns(),但不能在EditorTeam中实现,然后当我有一个AbstractTeam时,我可以在访问方法之前将其强制转换为Team。
我可以在AbstractTeam中实现addSpawn()和getSpawns(),使它们什么也不做,然后覆盖Team中的那些。这将消除对演员的需要,但会建议EditorTeam以某种方式关心产生,因为它现在将有两个(虚拟)方法。
所以我的问题是哪个更好?
代码是用C ++编写的,如果上面的内容不清楚,我可以提供一些示例。
答案 0 :(得分:1)
您可能会想到另一种选择。如果您将团队视为团队与一系列能力相关联,例如处理产卵的能力。并且能力在团队中注册。因此,您可以与团队一起工作,只有在有能力的情况下才能证明。
这种注册设计的外观(例如依赖注入或其他)是另一回事。
在游戏方面,团队,玩家或其他数据结构总是会随着时间的推移而发展,因此更方便的是将事物打包在非常静态的类层次结构中以便更灵活。它将导致一个不太复杂的模型,最终导致更少的痛苦。
提出你的问题。如果我只限于这两个选项,我更喜欢第一个,因为我不想拥有一个类,它拥有我在每个子类中实际上不需要的大量方法。幸运的是,您不必将子类转换为父类,以便将它们放入集合中。
答案 1 :(得分:1)
使用数字1.这就是dynamic_cast
的用途。您绝对不应该在基类中定义所有派生类不会实现的成员。
答案 2 :(得分:0)
在AbstractTeam类中使用虚拟方法。
public interface ISpawn
{
void Spawn1();
void Spawn2();
}
public class Team : AbstractTeam, ISpawn
{
// implement ISpawn and AbstracTeam in here...
}
public class EditorTeam : AbstractTeam
{
// implement AbstracTeam in here...
}
// usage....
ISpawn team = getSpawn();
if(team != null)
{
team.Spawn1();
team.Spawn2();
}
答案 3 :(得分:0)
你应该问自己的问题是;在Team
类之外的任何其他地方我是否需要add / getSpawn功能?如果不是,那就留在那里
如果是,那么在某个AbstractSpawnListenerTeam
类中移动该功能(理想情况下它不应该实现AbstractTeam
)并使团队继承它(以及实现AbstractTeam
)
如果你想重用产卵功能,那么组合比继承更好。仅为实际接口的包装实现部分保留继承
如果你想用继承来做,请记住,与其他语言一样,只要你从最多一个实现(AbstractSpawnListenerTeam
)和一个或多个接口继承,c ++中的多继承是安全的
如果您确实需要一个继承路径,请AbstractSpawnListenerTeam
实现AbstractTeam
,但保留所有虚拟方法= 0
答案 4 :(得分:0)
我有一个类似的问题
答案 5 :(得分:0)
您可能需要考虑第三种选择。如果跟踪生成点真的是与AbstractTeam
的想法分开的话,你可以将它从类层次结构中拉出来。
一种方法是让Team
从接口类继承,如下所示:
class ISpawnTracker
{
public:
Point SpawnPoint()=0;
};
class Team : public ISpawnTracker, public AbstractTeam
{
Point SpawnPoint()
{
// ...
}
};
这使您可以通过执行dynamic_cast<ISpawnTracker>
并测试NULL
来有条件地调用方法。
另一种方法是将生成点跟踪分解为自己的类,并在创建对象时注入一个; Team
类现在使用 SpawnTracker
类对其进行生成点跟踪,而EditorTeam
根本不需要了解它:
class ISpawnTracker
{
public:
void TrackSpawnPoints()=0;
};
class ConcreteSpawnTracker : public ISpawnTracker { /* ... */ };
class TestingSpawnTracker : public ISpawnTracker { /* ... */ };
class Team : AbstractTeam
{
public:
Team(ISpawnTracker *spawnTracker)
: mSpawnTracker(spawnTracker)
{ /* ... */ }
private:
ISpawnTracker mSpawnTracker;
};
最后一种方法的另一个好处是可以使Team
和ConcreteSpawnTracker
更容易进行单元测试,