2011-07-16 22 views
11

He estado leyendo acerca de las variables globales y lo malas que son, pero estoy atrapado en un lugar debido a eso. Voy a ser muy específico acerca de si debería usar variables globales en este escenario.¿Debo usar variables globales?

Estoy trabajando en un motor de juegos. Y mi motor se compone de muchos gerentes. Los administradores realizan ciertas tareas: almacenan recursos, los cargan, los actualizan, etc.

He convertido a todos mis administradores en singleton porque hay tantas clases y funciones que necesitan acceso a ellos. Estaba pensando en eliminar el singleton pero no sé cómo no puedo tenerlo y obtener acceso a estos gerentes.

Aquí es un ejemplo de lo que estoy tratando de decir (im malo en Inglés, lo siento):

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()); 
    } 

}; 

Entidad 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. 
} 

Aplicación

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

    return app->run(); 
} 

Como se puede ver que ni siquiera estoy incluyendo los archivos anteriores en mi archivo ScriptComponent.cpp que me gana un poco de tiempo de compilación. ¿Cómo puedo obtener ese tipo de resultado sin globales que hará que sea fácil de integrar como este. El singleton no es seguro para subprocesos, pero agregar hilos no llevará mucho tiempo.

Espero haber podido explicar el problema.

Gracias de antemano,
Gasim Gasimzada

+3

No hay misterio. La forma de evitar los globales es no tenerlos. Si la función A necesita acceder al objeto B, y B no es global, entonces B no ha pasado como argumento a A. Y así es como lo haces si no tienes valores globales. Pase los objetos necesarios como argumentos de función (o constructor). – jalf

+0

Como no puedo comentar sobre sus publicaciones, voy a comentar aquí. @jalf realmente entiendo que los globales son malos y una solución alternativa y robusta es pasar los valores a los argumentos de funciones o c'tors. ¿Pero tengo dos grandes problemas en ese caso? ¿De dónde estoy obteniendo esos gerentes (en realidad son solo envoltorios para facilitar la codificación)? @Dave dijo que el objeto 'ejecutivo' (vamos a llamarlo Engine) que almacena todos estos objetos y pasa las referencias a la función o c'tor. ¿Pero cómo lo estoy haciendo? Quiero que sea más fácil y más limpio. – Gasim

+0

En realidad, no importa. He encontrado una manera :) La única razón por la que quería usar singletons es porque tendré que tener muy poca dependencia en cada archivo fuente, pero ahora he encontrado una manera :) ¡Gracias chicos por toda su ayuda! Ahora volviendo al trabajo. – Gasim

Respuesta

26

no digo que debiera nunca globales uso, pero

:
  • Nunca únicos de uso. Here es por qué. Son horribles, y son mucho peores que simples viejos globals.
  • Las clases de "Administrador" son malas. ¿Qué es lo que "administran"? ¿Cómo lo "administran"? Las clases de "Gerente" deben dividirse en algo que puedas describir. Una vez que haya descubierto lo que significa "administrar" un objeto, puede definir uno o más objetos con responsabilidades mejor definidas.
  • Cuando usa globales, no los haga mutables. Un global de solo escritura puede ser aceptable (considere un registrador. Usted escribe en él, pero su estado nunca afecta la aplicación), y los valores globales de solo lectura también pueden ser aceptables (considere varias constantes que nunca cambian, pero que frecuentemente necesito leer de). Donde los globales se vuelven dañinos es cuando tienen un estado mutable: cuando ambos leen y escriben desde ellos.

Y, por último, la muy simple alternativa: Simplemente pase las dependencias como argumentos. Si un objeto necesita algo para funcionar, páselo a ese "algo" en su constructor. Si una función necesita algo para funcionar, páselo como un argumento.

Esto podría sonido como un montón de trabajo, pero no lo es. Cuando su diseño está atestado de globales e individuales, obtiene una enorme y enorme arquitectura de espagueti donde todo depende de todo lo demás. Debido a que las dependencias no son explícitamente visibles, usted obtiene descuidado, y en lugar de pensando en cuál es la mejor forma de conectar dos componentes, simplemente haga que se comuniquen a través de uno o más valores globales. Una vez que tiene que pensar en qué dependencias pasar explícitamente, la mayoría de ellas resultan innecesarias, y su diseño se vuelve mucho más limpio, más legible y fácil de mantener, y mucho más fácil de razonar. Y su número de dependencias disminuirá drásticamente, por lo que solo necesitará pasar un argumento extra o dos a una pequeña cantidad de objetos o funciones.

+1

Sin embargo, std :: cout es un objeto global no const. Creo que todo depende del comportamiento o la naturaleza de ese objeto global, o el contexto de su programa. ¿Ese objeto tiene una interfaz muy pequeña o tiene un comportamiento muy compacto? Ok, tal vez un global no es una mala idea en absoluto. –

3

no lo utilices variables globales. Si necesita un objeto de un tipo, luego lo pasa, por referencia si es necesario.

+3

Esta afirmación excesivamente simple; por ejemplo, puede ser más claro tener un objeto 'logger' global al que puedan llamar diferentes métodos. registrar la información de depuración en lugar de pasar una referencia al objeto logger a _every_ otro objeto en la aplicación. – DaveR

+3

-1 por predicar una buena pauta como imperativo absoluto y sin darle ningún razonamiento absoluto. No digo que los globales sean buenos, o incluso aceptables en la mayoría de los casos, * porque * estoy de acuerdo en que los globales son malos, quiero que la guía se presente y se respalde mejor. – delnan

+3

Puede ser demasiado simplista, pero el consejo siempre lo es. Los límites de velocidad en las carreteras son excesivamente simplistas también, porque proporcionar una cuenta completa y precisa de cuándo y cómo es seguro conducir exactamente qué tan rápido es poco realista. El punto en el consejo es proporcionar una regla simple que pueda usar como una guía. Nunca puede tener * todo * en cuenta, pero como regla general, "no usar variables globales" es perfectamente razonable.Y @delnan: revocar una respuesta con la que usted está de acuerdo, solo porque no está satisfecho con el razonamiento, es bastante dura. :) – jalf

4

¿Qué tal si eliminamos la clase base ScriptManager y usamos métodos estáticos en las clases de especialización? Parece que no hay ningún estado involucrado con ningún ScriptManagers, y no hay un patrimonio real que no sean las funciones puramente virtuales.

No pude deducir de las muestras de tu código si realmente usas polimorfismo aquí. Si no, las funciones de miembro estático me parecen correctas.

+1

gracias por la respuesta. De hecho, he cambiado mi código a algo como esto. y se deshizo de todos los singletons. – Gasim

+2

@Gasim ¡Agradable! ¡Parece que tengo la respuesta correcta aquí! (= – Gabriel