Должен ли я использовать глобальные переменные? - PullRequest
11 голосов
/ 16 июля 2011

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

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

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

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

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, который выигрывает у меня время компиляции.Как я могу получить такой результат без глобальных переменных, которые упростят интеграцию как этот.Синглтон не безопасен для потоков, но добавление потоков не займет много времени.

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

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

Ответы [ 3 ]

26 голосов
/ 16 июля 2011

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

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

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

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

4 голосов
/ 16 июля 2011

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

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

2 голосов
/ 16 июля 2011

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

...