Должен ли я использовать глобальные переменные?

Я читал о глобальных переменных и о том, насколько они плохи, но я застрял в одном месте из-за этого. Я буду очень конкретным, если я буду использовать глобальные переменные в этом сценарии.

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

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

Вот пример того, что я пытаюсь сказать (им плохо на английском, извините):

Singleton.h

template<class T> class Singleton {
private:
    Singleton( const Singleton& );
    const Singleton& operator=( const Singleton& );

protected:
    Singleton() { instance = static_cast<T*>(this); }
    virtual ~Singleton() {}

protected:
    static T * instance;

public:
    static T &Instance() {
        return *instance;
    }

};

ScriptManager.h

class ScriptManager : public Singleton<ScriptManager> {
public:
    virtual void runLine(const String &line)=0;
    virtual void runFile(const String &file)=0;
};

PythonScriptManager.cpp

class PythonScriptManager : public ScriptManager {
public:
    PythonScriptManager() { Py_Initialize(); }
    ~PythonScriptManager() { Py_Finalize(); }

    void runFile(const String &file) {
        FILE * fp = fopen(file.c_str(), "r");
        PyRun_SimpleFile(fp, file.c_str());
        fclose(fp);
        fp=0;
    }

    void runLine(const String &line) {
        PyRun_SimpleString(line.c_str());   
    }

};

Entity ScriptComponent

#include <CoreIncludes.h>
#include <ScriptManager.h>
#include <ScriptComponent.h>

void update() {

    ScriptManager::Instance().runFile("test_script.script");
    //i know its not a good idea to open the stream on every frame but thats not the main concern right now.
}

Приложение

int main(int argc, const char * argv) {
    Application * app = new Application(argc, argv);
    ScriptManager * script_manager = new PythonScriptManager;
    //all other managers

    return app->run();
}

Как вы видите, я даже не включил файлы выше в файл ScriptComponent.cpp, который выигрывает у меня некоторое время компиляции. Как я могу получить такой результат без globals, который упростит интеграцию в качестве этого. Синглтон не является потокобезопасным, но добавление потоков не займет много времени.

Надеюсь, я мог бы объяснить эту проблему.

Спасибо заранее,
Гасим Гасымзада

Ответ 1

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

  • Никогда не используйте сингл. Вот почему. Они ужасны, и они намного хуже, чем простые старые глобалы.
  • Классы "Менеджер" плохие. Что они "управляют"? Как они "управляют" им? Классы "Менеджер" должны быть разбиты на то, что вы можете описать. Как только вы выяснили, что значит "управлять" объектом, вы можете определить один или несколько объектов с более четко определенными обязанностями.
  • Когда вы используете глобальные переменные, не делайте их изменяемыми. Глобальный доступ, основанный на записи, может быть приемлемым (рассмотрите регистратор. Вы пишете ему, но его состояние никогда не влияет на приложение), и глобальные переменные, доступные только для чтения, могут быть в порядке (рассмотрите различные константы, которые никогда не изменяются, но которые вы часто необходимо прочитать). Там, где глобалы становятся вредными, это когда они имеют изменяемое состояние: когда вы оба читаете и пишите от них.

И, наконец, очень простая альтернатива: Просто передайте зависимости в качестве аргументов. Если объект нуждается в чем-то, чтобы функционировать, передайте ему что-то в своем конструкторе. Если функции нужно что-то для работы, передайте ей что-то "как аргумент".

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

Ответ 2

Как удалить базовый класс ScriptManager и использовать статические методы в классах специализации? Похоже, что с каким-либо ScriptManagers не существует никакого государства, и нет реального наследия, кроме чисто виртуальных функций.

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

Ответ 3

Никогда не используйте глобальные переменные. Если вам нужен объект типа, тогда вы передаете его, по ссылке, если необходимо.