Правильный способ оформления объекта - PullRequest
3 голосов
/ 29 июня 2011

В моей игре Bitfighter у меня есть класс с именем AbstractTeam, который имеет два подкласса: Team и EditorTeam.У них много общих методов, но Team отслеживает точки появления (реализуя методы addSpawn () и getSpawns ()), тогда как EditorTeam совершенно не заботится о точках появления.

Существует два варианта реализации этого:

  1. Я мог бы реализовать addSpawn () и getSpawns () в Team, но не в EditorTeam, тогда, когда у меня есть AbstractTeam, я мог бы привести его к Team перед доступом к методам.

  2. Я мог бы реализовать addSpawn () и getSpawns () в AbstractTeam, заставить их ничего не делать, а затем переопределить их в Team.Это исключило бы необходимость приведения, но предположило бы, что EditorTeam каким-то образом заботился о порождении, потому что теперь у него будут два (фиктивных) метода.

Так что мой вопрос, который лучше?

Код написан на C ++, и я могу предоставить несколько примеров, если вышеприведенное не ясно.

Ответы [ 6 ]

1 голос
/ 29 июня 2011

Используйте номер 1. Для этого dynamic_cast. Вы определенно не должны определять членов в вашем базовом классе, которые все производные классы не будут реализовывать.

1 голос
/ 29 июня 2011

Есть еще один вариант, о котором вы можете подумать. Если вы видите Команды как Команды, связанные с кучей способностей, таких как, например, способность справляться с нерестом. И способности регистрируются в командах. Таким образом, вы можете работать со своей командой все время и только доказать, что у них есть доступные способности.

Как будет выглядеть этот дизайн регистрации (например, внедрение зависимости или что-то еще) - это совсем другое.

В играх всегда случается, что команды, игроки или другие структуры данных со временем будут развиваться, поэтому удобнее собирать вещи не в очень статичной иерархии классов, чтобы быть более гибкими. Это приведет к менее сложной модели, которая в конечном итоге приведет к меньшему количеству боли.

К вашему вопросу. Я бы предпочел первый, если я ограничен этими двумя вариантами, потому что я не хочу иметь класс, который содержит тонны методов, которые мне на самом деле не нужны в каждом подклассе. И, к счастью, вам не нужно приводить подклассы к родительским классам, чтобы поместить их в коллекцию.

0 голосов
/ 30 июня 2011

Есть третий вариант, который вы можете рассмотреть. Если отслеживание точек появления действительно является чем-то отдельным от идеи 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 легче тестировать на единицу,

0 голосов
/ 30 июня 2011

у меня был похожий вопрос

Тестирование класса c ++ для функций

0 голосов
/ 29 июня 2011

вопрос, который вы должны задать себе; мне нужна функциональность add / getSpawn в любом другом месте, кроме класса Team? если нет, то оставь это там

если да, то переместить эту функциональность в некоторый класс AbstractSpawnListenerTeam (в идеале он не должен реализовывать AbstractTeam) и заставить команду наследовать от него (а также реализовать AbstractTeam)

если вы хотите повторно использовать функцию порождения, композиция намного лучше, чем наследование. Оставьте наследование только для части реализации реализаций реальных интерфейсов

если вы хотите сделать это с наследованием, помните, что, как и в других языках, множественное наследование в c ++ безопасно, если вы наследуете не более одной реализации (AbstractSpawnListenerTeam) и одного или нескольких интерфейсов

если вам абсолютно необходимо иметь один путь наследования, заставьте AbstractSpawnListenerTeam внедрить AbstractTeam, но оставьте все виртуальные методы = 0

0 голосов
/ 29 июня 2011

В вашем классе 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...