Удаление циклических зависимостей между классами в C ++ - PullRequest
0 голосов
/ 28 ноября 2018

Предположим, мы делаем карточную игру для двух игроков, и у нас есть классы Game, Player и Card.Игра содержит указатель на двух игроков и предоставляет интерфейс для игроков.Игрок состоит из здоровья игрока и его магии вместе с вектором карт, который является их рукой.Карта - это абстрактный класс.Каждая карта стоит волшебства, и ее можно разыграть.

Проблема состоит в том, что каждая карта при игре может изменить состояние игры любым количеством способов.Например, у нас может быть Карта, которая удваивает здоровье игрока, Карта, которая отравляет вражеского игрока на два хода, Карта, которая уничтожает всех миньонов на доске, Карта, которая создает копию каждого миньона, уже находящегося на доске, и т. Д.Возможности действительно безграничны.Единственное решение, которое я вижу, - наличие указателя на игру внутри каждой карты, но это выглядит довольно не элегантно.

Есть ли другие решения?

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

Всякий раз, когда у вас есть перекрестные зависимости, такие как:

class Card
{
   Game* game;
   void f() {
     game->method1();
   }
}; 

class Game
{
   std::vector<Card> cards;
 public:
   void method1();
};

Одна из зависимостей должна реализовывать интерфейс, подобный:

class IGame
{
 public:
   virtual void method1()=0;
};

class Card
{
  IGame* game;
  void f() {
     game->method1();
   }
}; 

class Game : public IGame
{
   std::vector<Card> cards;
 public:
   virtual void method1();
};

И, как вы можете видеть, больше нет перекрестных зависимостей.

0 голосов
/ 28 ноября 2018

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

Итак, в вашем примере, скажем, у вас есть класс Card, который является экземпляром существующегокласс.

Первое, что вы можете сделать, это отделить эффект карты от самой карты, например:

class CardEffect {
 // TODO ...
};

class Card {
private:
  const CardEffect* effect;
};

Так что теперь самой карте не нужно знатьчто-нибудь об игре.Но давайте углубимся в это: CardEffect не нужно знать каждую деталь класса Game, он должен уметь применять эффекты к игре.Это можно сделать, предоставив отдельный интерфейс с эффектом, который показывает только то, что необходимо для применения эффекта, и что-то вроде этого.

class GameInterface {
public:
  virtual const std::vector<Player*>& getPlayers() = 0;

  virtual damagePlayer(Player* player, int amount) = 0;
  virtual applyPeriodicDamage(Player* player, int turns, int amount) = 0

  ..
};

class CardEffect {
  virtual applyEffect(GameInterface* interface) = 0;
};

class Card {
private:
  const CardEffect* effect;
};

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

...