Évitez d'avoir une méthode d'initialisation

12

J'ai ce code existant où ils ont une classe et une méthode d'initialisation dans cette classe. Il est prévu qu'une fois l'objet de la classe créé, ils doivent appeler initialize dessus.

Raison pour laquelle la méthode initialize existe L'objet est créé tôt pour avoir une portée globale, puis la méthode initialize est appelée plus tard après le chargement d'une DLL dont elle dépend.

Problème avec l'initialisation La classe a maintenant ce bool isInitialized qui doit être vérifié dans chaque méthode avant de continuer et renvoie une erreur si elle n'est pas initialisée. Autrement dit, c'est une grande douleur.

Une solution possible Initialiser dans le constructeur. Ayez juste un pointeur sur l'objet dans la portée globale. Créez l'objet réel après le chargement de la DLL.

Problème avec la solution ci-dessus Quiconque crée un objet de cette classe doit savoir qu'il doit être créé uniquement après le chargement de la DLL, sinon il échouera.

Est-ce acceptable?


la source
J'ai eu ce même problème lors de la création d'objets liés à OpenGL qui doivent être instanciés avant qu'un contexte OpenGL existe, mais sont censés contenir des objets dépendants d'OpenGL comme des listes d'appels, des textures, etc.
3
Question stupide n ° 1: pourquoi le constructeur d'objet ne peut-il pas charger la DLL si elle n'est pas déjà chargée?
John R. Strohm
1
Excusez-moi d'avoir ressuscité une vieille question, mais au cas où quelqu'un lirait ceci en 2017, utilisez-le call_onceen C ++ 11 . Les projets qui ne sont pas encore en C ++ 11 devraient étudier comment call_once est implémenté en C ++ 11 (se concentrer sur le problème qu'il résout, puis comment), puis le réimplémenter dans leur version (périmée) de C ++. Il a besoin d'une primitive de synchronisation sécurisée multi-thread, dont l'état doit être initialisé statiquement (avec une valeur constante). Notez que les compilateurs pré-C ++ 11 peuvent avoir d'autres idiosyncrasies qui doivent être satisfaites.
rwong

Réponses:

7

Cela ressemble à un travail pour un proxy virtuel.

Vous pouvez créer un proxy virtuel contenant une référence à l'objet en question. Bien que la DLL ne soit pas chargée, le proxy peut offrir un certain comportement par défaut aux clients.Une fois la DLL chargée, le proxy transmettra simplement toutes les demandes au sujet réel.

Le proxy virtuel sera chargé de vérifier l'initialisation de la DLL et en fonction de cela, décider si la demande doit être déléguée au sujet réel.

Modèle de proxy dans Wikipedia

Comment aimez-vous cette idée?

edalorzo
la source
vous ne résolvez pas vraiment beaucoup ici. pour chaque méthode ajoutée dans l'objet réel, vous devez également ajouter cette méthode au proxy. Non?
Cela évite le problème de se rappeler d'appeler l'init. , mais il semble que chaque fonction du proxy doit encore vérifier si le fichier .dll est chargé.
1
@Sriram il y a maintenant une grande différence, en utilisant un proxy, vous avez limité la maintenance liée à la vérification des DLL à une seule classe: le proxy. Les clients de la classe n'auront même pas besoin de connaître la vérification des DLL. Puisque la plupart des méthodes font la délégation, je ne vois pas beaucoup de problème à devoir implémenter l'interface à la fois dans le vrai sujet du proxy. En d'autres termes, vous pouvez empêcher la création du vrai sujet jusqu'à ce que vous soyez sûr que la DLL a été chargée, puis vous n'aurez pas besoin de vérifier l'état de la DLL à chaque fois.
@edalorzo: alors que l'idée est bonne, le problème ici est que nous parlons déjà d'un proxy, et l'OP se plaint d'avoir à vérifier chaque méthode de son proxy ...
Matthieu M.
4

Rien d'utile ne se produit si la DLL n'est pas encore chargée; l'objet ne génère qu'une erreur. Est-ce une erreur fatale? Comment l'erreur est-elle gérée?

Votre méthodologie de test garantit-elle que cette erreur ne se produit jamais dans le produit fini?

Cela ressemble à un travail de test, pas de conception architecturale. Ne vous contentez pas de renvoyer une erreur, assertcela n'arrive jamais.

Corrigez tous les clients de la classe qui interceptent et ignorent actuellement l'erreur pour ne pas essayer de la provoquer en premier lieu.

Un modèle multithread peut être de définir une variable de condition partagée après le chargement de la DLL et de faire attendre le constructeur de l'objet (et de bloquer les autres threads) jusqu'à ce que la DLL soit chargée.

Potatoswatter
la source
Je pense que vous avez raison, il n'y a rien de mal à lever une exception lors de la création de l'objet en question si la DLL n'a pas été correctement initialisée. Le code client n'aurait alors besoin que de traiter cette exception et les tests devraient garantir que l'exception est correctement gérée.
2

Première chose: éviter les objets globaux comme nuisibles, à moins que leur état ne change jamais (configuration).

Maintenant, si vous êtes coincé avec cela, pour quelque raison que ce soit, il existe plusieurs modèles qui pourraient probablement vous aider.

J'ai eu deux idées:

  1. Utilisez une façade pour charger la DLL. L'objet n'est accessible que via la façade, la façade charge la DLL lors de la création et instancie l'objet en même temps.

  2. Utilisez un proxy, mais le type intelligent;)

Permettez-moi de développer le deuxième point, car je crains que la réponse de @edalorzo ne vous ait effrayé:

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

Maintenant, vous n'avez qu'un seul chèque.

Cela peut également être fait via une sorte de pointeur intelligent:

Pointer<Object> o;

Ici, vous ne réservez que de l'espace pour un pointeur, initialement nul, et ce n'est que lorsque la DLL est chargée que vous allouerez réellement l'objet. Tous les accès précédents doivent déclencher une exception (NullException: p?) Ou terminer le programme (proprement).

Matthieu M.
la source
1

C'est une question délicate. Je pense que l'architecture peut aider à résoudre le problème.

D'après les preuves, il semble que le fichier .dll ne soit pas chargé lorsque le programme est chargé. Cela ressemble presque à un plugin.

Une chose qui me vient à l'esprit est que vous devez initialiser le fichier .dll. Le programmeur doit supposer que l'objet ne reviendra pas de manière fiable, de toute façon. Vous pourrez peut-être en profiter ( bool isObjectLoaded() { return isInitialized; }), mais vous obtiendrez certainement plus de rapports de bogues lors de vos vérifications.

Je pensais à un singleton.

Si vous appelez le singleton, il doit renvoyer l'objet correct s'il peut en récupérer un. S'il ne peut pas renvoyer l'objet correct, vous devriez obtenir une valeur d'erreur / nullptr / objet de base vide. Cela ne fonctionnerait pas si l'objet pouvait mourir sur vous, cependant.

De plus, si vous avez besoin de plusieurs instances de l'objet, vous pouvez utiliser quelque chose comme une usine à la place.

Joe Plante
la source