C ++: я должен использовать глобальные переменные или указатели членов класса для связи между модулями? - PullRequest
4 голосов
/ 02 марта 2011

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

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

Я в основном вижу здесь три возможных решения:

  • Если подсистеме X необходим доступ к подсистеме Y, добавьте переменную-член в класс X, указывая на экземплярof Y. Когда X создан, передайте ему указатель на Y и установите переменную-член m_pSystemY.

  • Объявите глобальную переменную CSystemX * g_SystemX для каждой подсистемы.Он будет заполнен указателем на только что созданный экземпляр подсистемы при запуске программы.Позже вы можете легко получить к нему доступ из любого места.

  • Создать сложный класс менеджера подсистем.Все подсистемы хранятся в массиве.Вам необходимо вызвать функцию для доступа к определенной подсистеме.

Мои вопросы:

  • Какое из этих решений я должен использовать дляпроект моего игрового движка?

  • Кто-нибудь из вас имеет личный опыт использования любого из этих методов?

Ответы [ 5 ]

2 голосов
/ 02 марта 2011

Как смешно, я вижу один ответ в каждом направлении!

В любом случае, что я рекомендую вам сделать, следуя опции № 1 (добавить переменную-член в класс X, указывая на экземпляр Y) с помощьюНебольшое исправление: используйте интерфейс вместо .

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

Когда создается модуль A, если ему нужно использовать функции модуля B, вы будете делать что-то вроде setModuleBHandler(moduleB).

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

Это позволит вам следовать другим рекомендациям, таким как TDD.

Пример:

Предположим, что модуль Broker нуждается в регистрации (и у вас есть модуль, предназначенный для регистрации).Вместо объявления public void setLogger(Logger logger) предпочтительнее объявить его следующим образом: public void setLogger(LoggerInterface logger).

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

Например, у вас может быть класс TextLogger, реализующий интерфейс, и другойDBLogger реализует тот же интерфейс.Вы можете поменять их местами, не ожидая каких-либо проблем.

Примечание: использование абстрактного класса даст вам почти такие же результаты.

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

2 голосов
/ 02 марта 2011

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

0 голосов
/ 02 марта 2011

Я бы пошел с классом менеджера подсистем. Я бы реализовал это с использованием шаблонов и убедился, что подсистемы реализуют один и тот же интерфейс (то есть c # lingo не уверен, что такое эквивалент в c ++), чтобы они все могли общаться друг с другом.

0 голосов
/ 02 марта 2011

Я бы вообще не использовал глобальные переменные.Я бы передавал указатели на другие системы через функцию инициализации.Кто бы ни создавал эти системы и инициализировал их, он может решить, как их хранить, будь то их собственные глобальные переменные, члены статического класса, одноэлементные оболочки или (что наиболее уместно) член некоторого объединяющего класса (IE: class CGame).В любом случае, передача их при инициализации является наиболее модульной, удобочитаемой и поддерживаемой без ущерба для простоты использования.

0 голосов
/ 02 марта 2011

Если у ваших классов всегда будет только один экземпляр, вы можете рассматривать их как одиночные:

class singleton
{
  // Private constructor
  singleton() {}
  // Static instance defaul to null in cpp
  static singleton *instance;

public:

  // Access function (put in cpp)
  static singleton* getInstance()
  {
    if(!instance) instance = new singleton();
    return instance;
  }
};
...