Evitare di avere un metodo di inizializzazione


12

Ho questo codice esistente in cui hanno una classe e un metodo di inizializzazione in quella classe. Si prevede che, una volta creato l'oggetto della classe, debbano chiamare l'inizializzazione su di esso.

Motivo per cui esiste il metodo di inizializzazione L'oggetto viene creato in anticipo per avere un ambito globale e quindi il metodo di inizializzazione viene chiamato in seguito dopo aver caricato una dll da cui dipende.

Problema con l'inizializzazione La classe ora ha questo bool isInitialized che deve essere verificato in ogni metodo prima di procedere e restituisce un errore se non è inizializzato. In poche parole, è un grande dolore.

Una possibile soluzione Inizializza nel costruttore. Avere solo un puntatore all'oggetto nell'ambito globale. Crea l'oggetto reale dopo il caricamento della dll.

Problema con la soluzione di cui sopra Chiunque crei un oggetto di questa classe deve sapere che deve essere creato solo dopo il caricamento della DLL, altrimenti fallirà.

È accettabile?


Ho avuto lo stesso problema durante la creazione di oggetti correlati a OpenGL che devono essere istanziati prima che esista un contesto OpenGL, ma dovrebbero contenere oggetti dipendenti da OpenGL come elenchi di chiamata, trame, ecc.

3
Stupida domanda n. 1: perché il costruttore di oggetti non può caricare la DLL se non è già caricata?
John R. Strohm,

1
Chiedere scusa per aver resuscitato una vecchia domanda, ma nel caso qualcuno la stia leggendo nel 2017, utilizzare call_oncein C ++ 11 . I progetti che non sono ancora su C ++ 11 dovrebbero studiare come call_once è implementato in C ++ 11 (concentrarsi su quale problema risolve, e poi come), e quindi implementarlo nuovamente nel loro (vecchio) sapore di C ++. È necessaria una primitiva di sincronizzazione sicura multi-thread, il cui stato deve essere inizializzato staticamente (con un valore costante). Si noti che i compilatori pre-C ++ 11 potrebbero avere altre idiosincrasie che devono essere soddisfatte.
rwong

Risposte:


7

Sembra un lavoro per un proxy virtuale.

È possibile creare un proxy virtuale contenente un riferimento all'oggetto in questione. Sebbene la DLL non sia caricata, il proxy potrebbe offrire determinati comportamenti predefiniti ai client, una volta caricata la DLL, il proxy inoltrerà semplicemente tutte le richieste all'oggetto reale.

Il proxy virtuale sarà responsabile del controllo dell'inizializzazione della DLL e in base a ciò decide se la richiesta deve essere delegata all'oggetto reale.

Modello proxy in Wikipedia

Ti piace questa idea?


non risolvi davvero molto qui. per ogni metodo aggiunto nell'oggetto reale, è necessario aggiungere anche quel metodo al proxy. No?

Questo evita il problema di ricordare di chiamare init. funzione, ma sembra che ogni funzione nel proxy debba ancora controllare per vedere se il .dll è caricato.

1
@Sriram ora c'è una grande differenza, usando un proxy hai limitato la manutenzione relativa al controllo DLL a una singola classe: il proxy. I client della classe non avranno nemmeno bisogno di conoscere il controllo DLL. Poiché la maggior parte dei metodi eseguirà la delega, non vedo molti problemi nel dover implementare l'interfaccia sia nell'oggetto reale nel proxy. In altre parole, è possibile impedire la creazione del soggetto reale fino a quando non si è sicuri che la DLL sia stata caricata, quindi non sarà necessario controllare lo stato DLL ogni volta.

@edalorzo: mentre l'idea è buona, il problema qui è che stiamo già parlando di un proxy e l'OP si lamenta di dover controllare ogni metodo del suo proxy ...
Matthieu M.

4

Non si verifica nulla di utile se la DLL non è ancora stata caricata; l'oggetto genera solo un errore. È un errore fatale? Come viene gestito l'errore?

La tua metodologia di test garantisce che questo errore non si verifichi mai nel prodotto finito?

Sembra un lavoro da testare, non per la progettazione architettonica. Non solo restituire un errore, assertche non succede mai.

Correggi tutti i client della classe che attualmente rilevano e ignorano l'errore per non provarlo e causarlo in primo luogo.

Un modello multithread potrebbe essere quello di impostare una variabile di condizione condivisa dopo aver caricato la DLL e far attendere il costruttore dell'oggetto (e bloccare altri thread) fino al caricamento della DLL.


Penso che tu abbia ragione, non c'è nulla di sbagliato nel lanciare un'eccezione alla creazione dell'oggetto in questione se la DLL non è stata inizializzata correttamente. Il codice client dovrebbe quindi solo gestire questa eccezione e i test dovrebbero garantire che l'eccezione sia gestita in modo appropriato.

2

Prima cosa: evitare gli oggetti globali come parassiti, a meno che il loro stato non cambi mai (configurazione).

Ora, se sei bloccato con esso, per qualsiasi motivo, ci sono diversi progetti che potrebbero aiutarti.

Ho avuto due idee:

  1. Utilizzare una facciata per caricare la DLL. L'oggetto è accessibile solo tramite la facciata, la facciata carica la DLL al momento della creazione e crea un'istanza dell'oggetto contemporaneamente.

  2. Utilizzare un proxy, ma il tipo intelligente;)

Consentitemi di approfondire il secondo punto, poiché temo che la risposta di @edalorzo potrebbe avervi spaventato:

// 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;
}

Ora hai un solo controllo.

Questo può essere fatto anche attraverso una sorta di puntatore intelligente:

Pointer<Object> o;

Qui si riserva solo spazio per un puntatore, inizialmente nullo, e solo quando viene caricata la DLL si alloca effettivamente l'oggetto. Tutti gli accessi precedenti dovrebbero sollevare un'eccezione (NullException: p?) O terminare il programma (in modo pulito).


1

Questa è una domanda difficile. Sento che l'architettura può aiutare con il problema.

Dalle prove, sembra che il file DLL non sia caricato quando viene caricato il programma. Sembra quasi un plugin.

Una cosa che viene in mente è che devi inizializzare il DLL. Il programmatore deve supporre che l'oggetto non ritorni in modo affidabile, comunque. Potresti essere in grado di trarre vantaggio da questo ( bool isObjectLoaded() { return isInitialized; }), ma otterrai sicuramente più segnalazioni di bug sui tuoi controlli.

Stavo pensando a un singleton.

Se si chiama il singleton, dovrebbe restituire l'oggetto corretto se è possibile recuperarne uno. Se non è possibile restituire l'oggetto corretto, è necessario ottenere un valore di errore / nullptr / oggetto base vuoto. Questo non funzionerebbe se l'oggetto può morire su di te, però.

Inoltre, se hai bisogno di più istanze dell'oggetto, puoi invece usare qualcosa come una Factory.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.