设计对象层次结构的正确方法

时间:2011-06-29 16:25:54

标签: c++ oop

在我的游戏Bitfighter中,我有一个名为AbstractTeam的类,它有两个子类:Team和EditorTeam。他们共享许多方法,但Team跟踪产生点(实现addSpawn()和getSpawns()方法),而EditorTeam根本不关心产生点。

我可以看到实现这个的两种设计:

  1. 我可以在Team中实现addSpawn()和getSpawns(),但不能在EditorTeam中实现,然后当我有一个AbstractTeam时,我可以在访问方法之前将其强制转换为Team。

  2. 我可以在AbstractTeam中实现addSpawn()和getSpawns(),使它们什么也不做,然后覆盖Team中的那些。这将消除对演员的需要,但会建议EditorTeam以某种方式关心产生,因为它现在将有两个(虚拟)方法。

  3. 所以我的问题是哪个更好?

    代码是用C ++编写的,如果上面的内容不清楚,我可以提供一些示例。

6 个答案:

答案 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)

我有一个类似的问题

Testing a c++ class for features

答案 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;
};

最后一种方法的另一个好处是可以使TeamConcreteSpawnTracker更容易进行单元测试,