Evita tener un método de inicialización

12

Tengo este código existente donde tienen una clase y un método de inicialización en esa clase. Se espera que una vez que se crea el objeto de la clase, necesiten llamar a initialize en él.

Motivo por el que existe el método de inicialización El objeto se crea temprano para tener un alcance global y luego se llama al método de inicialización más tarde después de cargar un archivo dll del que depende.

Problema con la inicialización La clase ahora tiene este bool isInitialized que debe verificarse en cada método antes de continuar y devuelve un error si no se inicializa. En pocas palabras, es un gran dolor.

Una posible solución Inicializar en el constructor. Tenga solo un puntero al objeto en el ámbito global. Cree el objeto real después de que se cargue el dll.

Problema con la solución anterior Cualquier persona que cree un objeto de esta clase debe saber que debe crearse solo después de cargar el archivo DLL o, de lo contrario, fallará.

¿Es esto aceptable?


fuente
He tenido este mismo problema al crear objetos relacionados con OpenGL que deben instanciarse antes de que exista un contexto OpenGL, pero se supone que contienen objetos dependientes de OpenGL como listas de llamadas, texturas, etc.
3
Pregunta estúpida # 1: ¿Por qué el constructor de objetos no puede cargar la DLL si aún no está cargada?
John R. Strohm
1
Pide disculpas por resucitar una vieja pregunta, pero en caso de que alguien esté leyendo esto en el año 2017, úsala call_onceen C ++ 11 . Los proyectos que aún no están en C ++ 11 deben estudiar cómo se implementa call_once en C ++ 11 (centrarse en qué problema resuelve y luego cómo), y luego volver a implementarlo en su sabor (obsoleto) de C ++. Necesita una primitiva de sincronización segura de subprocesos múltiples, cuyo estado debe inicializarse estáticamente (con un valor constante). Tenga en cuenta que los compiladores anteriores a C ++ 11 pueden tener otras idiosincrasias que deben satisfacerse.
rwong

Respuestas:

7

Suena como un trabajo para un proxy virtual.

Puede crear un proxy virtual que contenga una referencia al objeto en cuestión. Si bien la DLL no está cargada, el Proxy podría ofrecer cierto comportamiento predeterminado a los clientes, una vez que se carga la DLL, el proxy simplemente reenviará todas las solicitudes al asunto real.

El proxy virtual será responsable de verificar la inicialización de la DLL y, en función de esto, decide si la solicitud debe delegarse en el asunto real.

Patrón de proxy en Wikipedia

¿Cómo te gusta esta idea?

edalorzo
fuente
realmente no resuelves mucho aquí. para cada método agregado en el objeto real, también necesitará agregar ese método al proxy. ¿No?
Esto evita el problema de recordar llamar al init. función, pero parece que todas las funciones en el proxy aún necesitan verificar para ver si el .dll está cargado.
1
@Sriram ahora hay una gran diferencia: al usar un proxy, ha restringido el mantenimiento relacionado con la comprobación de DLL a una sola clase: el proxy. Los clientes de la clase ni siquiera necesitarán saber sobre la comprobación de DLL. Dado que la mayoría de los métodos harán delegación, no veo mucho problema en tener que implementar la interfaz tanto en el sujeto real en el proxy. En otras palabras, puede evitar la creación del asunto real hasta que esté seguro de que la DLL se ha cargado, y luego no necesitará verificar el estado de la DLL cada vez.
@edalorzo: aunque la idea es buena, el problema aquí es que ya estamos hablando de un proxy, y el OP se queja de tener que verificar cada método de su proxy ...
Matthieu M.
4

No ocurre nada útil si la DLL aún no está cargada; El objeto solo genera un error. ¿Es un error fatal? ¿Cómo se maneja el error?

¿Su metodología de prueba garantiza que este error nunca ocurra en el producto terminado?

Parece un trabajo de prueba, no de diseño arquitectónico. No solo devuelva un error, asserteso nunca sucede.

Arregle a cualquier cliente de la clase que actualmente detecte e ignore el error para no intentarlo y causarlo en primer lugar.

Un patrón multiproceso podría ser establecer una variable de condición compartida después de cargar la DLL, y hacer que el constructor del objeto espere (y bloquee otros hilos) hasta que se cargue la DLL.

Agua de patata
fuente
Creo que tiene razón, no hay nada de malo en lanzar una excepción al crear el objeto en cuestión si la DLL no se ha inicializado correctamente. El código del cliente solo tendría que lidiar con esta excepción y las pruebas deberían garantizar que la excepción se maneje adecuadamente.
2

Primero: evite los objetos globales como la plaga, a menos que su estado nunca cambie (configuración).

Ahora, si está atrapado con él, por cualquier razón, hay varios diseños que probablemente podrían ayudarlo.

Se me ocurrieron dos ideas:

  1. Use una fachada para cargar la DLL. Solo se puede acceder al Objeto a través de la Fachada, la Fachada carga la DLL después de la creación y crea una instancia del Objeto al mismo tiempo.

  2. Use un Proxy, pero del tipo inteligente;)

Permítanme dar más detalles sobre el segundo punto, ya que temo que la respuesta de @edalorzo pueda haberlos asustado:

// Private
static Object& GetObjectImpl() { static Object O; return O; }

// Public
static Object& GetObject() {
  Object& o = GetObjectImpl();
  assert(o.isInitialized() && "Object not initialized yet!");
  return o;
}

Ahora solo tiene un cheque único.

Esto también se puede hacer a través de algún tipo de puntero inteligente:

Pointer<Object> o;

Aquí solo reserva espacio para un puntero, inicialmente nulo, y solo cuando se carga la DLL realmente asignará el objeto. Todos los accesos anteriores deben generar una excepción (NullException: p?) O finalizar el programa (limpiamente).

Matthieu M.
fuente
1

Esta es una pregunta difícil. Siento que la arquitectura puede ayudar con el problema.

Según la evidencia, parece que .dll no se carga cuando se carga el programa. Casi suena como un complemento.

Una cosa que viene a la mente es que debe inicializar el .dll. El programador tiene que suponer que el objeto no volverá de manera confiable, de todos modos. Es posible que pueda aprovechar esto ( bool isObjectLoaded() { return isInitialized; }), pero definitivamente obtendrá más informes de errores sobre sus comprobaciones.

Estaba pensando en un singleton.

Si llama al singleton, debería devolver el objeto correcto si puede recuperar uno. Si no puede devolver el objeto correcto, debería obtener algún valor de error / nullptr / objeto base vacío. Sin embargo, esto no funcionaría si el objeto puede morir en ti.

Además, si necesita varias instancias del objeto, puede usar algo como una Fábrica en su lugar.

Joe Plante
fuente