2011-04-12 3 views
0

Je suis confronté à un problème et je cherche le meilleur concept/théorie de solution.Gestion des modules et des classes (liaison dynamique)

J'ai un système qui a besoin d'utiliser des objets. Chaque objet que le système utilise possède une interface connue, probablement implémentée en tant que classe abstraite. Les interfaces sont connues au moment de la construction et ne changeront pas. La mise en œuvre exacte à utiliser variera et je n'ai aucune idée à l'avance du module qui va le fournir. La seule garantie est qu'ils fourniront l'interface. Le nom de la classe et le module (DLL) proviennent d'un fichier de configuration ou peuvent être modifiés par programmation.

Maintenant, j'ai tout cela mis en place au moment en utilisant un système relativement simple, mettre en place quelque chose comme si (réécrite pseudo-code, juste pour montrer les bases):

struct ClassID 
{ 
    Module * module; 
    int number; 
}; 

class Module 
{ 
    HMODULE module; 
    function<void * (int)> * createfunc; 

    static Module * Load(String filename); 

    IObject * CreateClass(int number) 
    { 
     return createfunc(number); 
    } 
}; 

class ModuleManager 
{ 
    bool LoadModule(String filename); 

    IObject * CreateClass(String classname) 
    { 
     ClassID class = AvailableClasses.find(classname); 
     return class.module->CreateObject(class.number); 
    } 

    vector<Module*> LoadedModules; 
    map<String, ClassID> AvailableClasses; 
}; 

Les modules ont quelques fonctions exportées pour donner le nombre de classes qu'elles fournissent et les noms/identifiants de celles-ci, qui sont ensuite stockées. Toutes les classes dérivent de IObject, qui a un destructeur virtuel, stocke le module source et a quelques méthodes pour obtenir l'ID de classe, quelle interface il implémente et tel.

Le seul problème avec ceci est que chaque module doit être chargé manuellement quelque part (répertorié dans le fichier de configuration, pour le moment). Je voudrais éviter de le faire explicitement (en dehors du , à l'intérieur que je ne suis pas vraiment concerné par la façon dont il est implémenté).

Je voudrais avoir un système similaire sans avoir à gérer le chargement des modules, juste créer un objet et (une fois que tout est configuré) il apparaît comme par magie.

Je crois que c'est similaire à ce que COM est censé faire, à certains égards. J'ai regardé brièvement dans le système COM, mais il semble être excessif au-delà de la croyance. J'ai seulement besoin des classes connues dans mon système et je n'ai pas besoin de toutes les autres fonctionnalités qu'il gère, juste des implémentations d'interfaces venant de quelque part. Mon autre idée est d'utiliser le registre et garder une clé avec toutes les classes connues/enregistrées et leurs modules et numéros source, donc je peux juste les rechercher et il apparaîtra que Manager::CreateClass trouve et rend l'objet magiquement. Cela semble être une solution viable, mais je ne sais pas si c'est optimal ou si je réinvente quelque chose. Donc, après tout cela, ma question est: Comment gérer cela? Existe-t-il une technologie existante, sinon, comment la configurer au mieux? Y a-t-il des pièges que je devrais surveiller?

Répondre

1

COM très probablement est ce que vous voulez. C'est très large mais vous n'avez pas besoin d'utiliser toutes les fonctionnalités. Par exemple, vous n'avez pas besoin d'obliger les participants à enregistrer des GUID, vous pouvez définir votre propre mécanisme de création d'instances d'interfaces. Il existe un certain nombre de modèles et d'autres mécanismes pour faciliter la création d'interfaces COM. De plus, puisqu'il s'agit d'une norme, il est facile de documenter les exigences.

Une chose très importante à garder à l'esprit est que l'importation/exportation d'objets C++ nécessite que tous les participants utilisent le même compilateur. Si vous pensez que cela pourrait vous poser un problème, vous devriez utiliser COM. Si vous acceptez cette restriction, vous pouvez continuer comme vous l'êtes.

+0

Certaines des fonctionnalités offertes par COM, comme ne pas se soucier des compilateurs ou des runtimes et je crois que l'utilisation de modules C# est très agréable, mais je n'ai pas encore trouvé d'informations sur la manière de faire fonctionner COM. Ce que j'ai pu trouver lors de la création de nouvelles interfaces/objets implique beaucoup de définition et de code standard. Avez-vous des suggestions sur de bons documents à regarder? – ssube

+0

Je pense que je regarderais ATL pour créer des objets COM. Je lirais aussi le livre de Don Box, Essential COM. En fait, vous n'avez besoin que des premiers chapitres. Ce n'est vraiment pas si effrayant! –

+0

Le livre a une bonne explication de la façon dont les choses fonctionnent, et certains docs MSDN font pour de beaux tutoriels. Je vais essayer et voir si ça marche. Merci pour les suggestions. :) – ssube

1

Je ne sais pas s'il existe une technologie pour cela. Je sais que j'ai travaillé avec un système très similaire à celui-ci. Nous avons utilisé des fichiers XML pour décrire les différentes classes mises à disposition par différents modules. Notre équivalent de ModuleManager analyse les fichiers xml pour déterminer ce qu'il faut créer pour l'utilisateur au moment de l'exécution en fonction du nom de classe qu'ils ont fourni et de la configuration du système. (Demander un objet qui a implémenté l'interface 'I' pourrait restituer n'importe lequel des objets 'A', 'B' ou 'C' selon la configuration du système.)

Le gros problème que nous avons trouvé était que le système était très fragile et parfois difficile à déboguer/comprendre. Juste en lisant le code, il était souvent presque impossible de voir quelle classe concrète était instanciée. Nous avons également constaté que la maintenance du code XML créait plus de bogues et de frais généraux que prévu.Si je devais le faire à nouveau, je conserverais le modèle de conception des classes d'exposition à partir des DLL via les interfaces, mais je n'essaierais pas de créer un registre central des classes, ni de tout dériver d'une classe de base telle que Je proteste.

Je voudrais plutôt rendre chaque module responsable de l'exposition de ses propres fonctions d'usine pour instancier des objets.

+0

Dans ma configuration actuelle, chaque module exporte 3 fonctions ('int ClassCount()', 'const char * ClassInfo (int)' et 'IObject * ClassCreate (int)'). Cela fournit un moyen facile d'éviter les définitions XML que vous décrivez et fournit l'usine intégrée, en quelque sorte. La base 'IObject' garantit simplement que tous les objets savent ce qu'ils sont et d'où ils viennent. – ssube

+0

Je n'aimais pas la classe de base commune dans notre système car elle conduisait à un grand nombre d'instructions dynamic_cast dans le code. Comme dans votre exemple, nous avons renvoyé tous les objets sous IObject *, puis nous les avons castés sur l'interface correcte. Je préfère que les fonctions d'usine renvoient des interfaces significatives, même si cela signifie que j'ai besoin de plusieurs fonctions. Cela dit, cela ne vous empêche certainement pas d'utiliser une classe de base commune si vous avez besoin de savoir d'où viennent les objets. – Nathanael

+1

Oui, je vois ce que vous voulez dire et comment cela pourrait avoir des avantages. Comment avez-vous géré savoir quels objets étaient disponibles (par nom) en général, cependant? – ssube

Questions connexes