2009-02-20 11 views
5

Estoy construyendo una aplicación C++, y tengo varios objetos de utilidad que todas mis clases deben usar. Estas son cosas como el objeto de registro, el objeto de estado global, el objeto DAL, etc ...¿Cuál es la mejor forma de manejar múltiples dependencias de objetos en C++?

Hasta este punto, he estado pasando todos estos objetos como referencias en los constructores de mi clase.

Por ejemplo:

 
class Honda : public Car 
{ 
    public: 
     Honda (const GlobalState & state, 
       const Log & logger, 
       const DAL & dal); 

    ... 

    private: 
     const GlobalState & my_state; 
     const Log & my_logger; 
     const DAL & my_dal; 
} 

esto se pone aburrido rápido, porque cada vez que agrega un objeto de utilidad que todas mis clases necesidad de acceder, tengo que ir y cambiar los constructores de todo el mundo.

He oído que la forma correcta de resolver este problema es crear una estructura que contenga todos los objetos de utilidad diferentes y pasarla (como referencia) a todos los objetos que necesiten acceder a ella.

¿Es esta la forma correcta de solucionar este problema? ¡Gracias!

ACTUALIZACIÓN: Gracias a todos por los comentarios. Después de un poco de investigación adicional, he decidido seguir usando Dependency Injection.

Respuesta

6

Puede hacer uso del Service Locator patrón. This article introduce tanto la inyección de dependencia (que está utilizando actualmente) como el localizador de servicios.

Sin embargo, considere esto: la idea de la inyección de dependencia es tener un sistema donde cada componente tenga una responsabilidad bien definida y minimice el conocimiento de otros componentes cuando sea posible. Si el componente necesita otros componentes para hacer su trabajo, estos se le pasan explícitamente. Esto hace que el componente sea más simple de entender, más probable que sea correcto y más fácil de mantener.

Si regularmente necesita agregar componentes que deben conocerse en todo el sistema, puede haber algún problema con el diseño del sistema (o la forma en que se le agregan nuevas funciones). El patrón de inyección de dependencia solo hace que este problema sea explícitamente visible.

2

Yo diría que tener fábricas con métodos de clases estáticas para crear objetos según sea necesario, si no se mantienen en estado, es probablemente una mejor solución. Si los objetos se necesitan en todas partes y necesitan mantener el estado global, entonces se puede justificar el uso del patrón de Singleton. Realmente necesitaría saber que los métodos de fábrica y la recreación de los objetos, según sea necesario, no funcionarían antes de mirar a Singleon. Usar Singleton complicará sus esfuerzos de prueba.

+0

Ese es un buen punto. En lo que respecta a estos objetos, realmente no quiero recrearlos más de una vez. Por ejemplo, cada vez que creaba un objeto DAL, de hecho estaba creando otra conexión a mi base de datos, algo que realmente no necesito. Del mismo modo, solo necesito un registrador. – Runcible

11

Usted puede hacer sus GlobalState, troncos, clases DAL únicos:

class GlobalState { 
public: 
    static GlobalState& getInstance(); 
protected: 
    GlobalState(); 
    GlobalState(const GlobalState&); 
    GlobalState& operator= (const GlobalState&); 
private: 
    static GlobalState* p_instance; 
}; 

static GlobalState* GlobalState::p_instance = NULL; 

/*static*/ 
GlobalState& getInstance() { 
    // TODO: acquire lock if multi-threaded 
    if (!p_instance) {     // first time? 
    p_instance = new GlobalState(); // create sole instance 
    } 
    // TODO: release lock if multi-threaded 
    return *p_instance;    // sole instance 
} 

Luego, dentro de sus diferentes métodos,

Honda::MyMethod() { 
    ... 
    const GlobalState& my_state = GlobalState::getInstance(); 
    ... 
} 

puede simplificar aún más su vida (y reducir la cantidad de código duplicación) por defining a singleton C++ template, bajo ciertas condiciones (por ejemplo, todos los constructores de clases tienen el mismo número y tipos de argumentos, etc.)

+0

if (p_instance) debería ser if (! P_instance) ¿verdad? –

+0

En realidad, a menos que mi memoria me falle, no puede confiar en que el puntero se inicie a nulo. Debe escribir en una variable de tipo estática e incrementarla después de la primera instanciación. –

+0

es un miembro estático. por lo que se inicializa en un puntero nulo (a menos que se inicialice explícitamente de otra manera; una buena idea para inicializarlo explícitamente a 0) –

1

Lo bueno En cuanto a crear objetos de estado en singletons, ya no tiene que pasar más referencias a ellos en todos sus constructores.

lo tanto, si se define su, estado global como sugiere Vlad R, que podría referirse a ella dentro de los constructores (y otras partes) utilizando, por ejemplo:

GlobalState::getInstance() 

y por lo tanto eliminar parámetros de constructores, así como las variables miembro, haciendo sus interfaces más limpias.

1

Utilizo instancias con nombre global para la mayoría de mis objetos de estado globales. El patrón es similar al singleton tradicional, excepto que con el singleton, los puristas insistirán en que su constructor también sea privado para evitar la construcción independiente, mientras que mi patrón permite la construcción independiente según sea necesario (es decir: mi plantilla singleton no requiere el constructor de la clase singleton ser privado). De lo contrario, el patrón es similar a lo que Vlad escribió.

Una cosa a tener en cuenta: si necesita desasignar sus singletons al salir del programa (por ejemplo, tiene un requisito impuesto que no pierde la memoria que asignó), el manejo puede ser muy complicado muy rápido. Describir todas las cosas que he tenido que rebuscar para intentar no tener pérdidas de memoria usando singletons tomaría mucho espacio, y muchas de ellas no son bonitas; por lo tanto, si necesita limpieza, considere usar gases globulares simples en su lugar (por más desagradable que sea).

0

Para responder a su pregunta, Michael, no son efectivamente cuatro cosas diferentes que se pueden hacer en esta situación:

  1. hacer todos sus servicios públicos objetos en globals.
  2. Cambie todas sus clases de utilidad en Singletons.
  3. Utilice un localizador de servicios.
  4. Uso inyección de dependencias (que ya lo están haciendo)

Vamos a abordar las tres primeras opciones:

  1. El uso de variables globales puede ser muy adecuado, pero oculta la dependencia de los objetos y hace que las pruebas de aislamiento más difícil.
  2. It's not a good idea to use Singletons.
  3. It's not a good idea to use a Service Locator.

Por lo tanto, keep using Dependency Injection.

Cuestiones relacionadas