Un thème récurrent dans mes travaux de développement a été l'utilisation ou la création d'une architecture de plug-in interne. Je l'ai vu approché de différentes manières: fichiers de configuration (XML, .conf, etc.), frameworks d'héritage, informations de base de données, bibliothèques, etc. Dans mon expérience:
En cherchant à apprendre des différentes architectures avec lesquelles j'ai travaillé, je me tourne également vers la communauté pour des suggestions. Comment avez-vous mis en œuvre une SOLID de plug-in? Quel a été votre pire échec (ou le pire que vous ayez vu))? Que feriez-vous si vous deviez implémenter un nouveau plug-in Quel est le meilleur exemple d’une bonne architecture dans quel SDK ou projet open source avec lequel vous avez travaillé?
Quelques exemples que j'ai trouvés par moi-même:
Ces exemples semblent jouer sur différentes forces linguistiques. Une bonne architecture de plugin est-elle nécessairement liée au langage? Est-il préférable d'utiliser des outils pour créer une architecture de plug-in ou de le faire sur ses propres modèles?
Ce n'est pas une réponse autant qu'un tas de remarques/exemples potentiellement utiles.
Un moyen efficace de rendre votre application extensible consiste à exposer ses éléments internes en tant que langage de script et à écrire tout le contenu de niveau supérieur dans ce langage. Cela le rend assez modifiable et pratiquement évolutif (si vos primitives sont bien choisies et implémentées). Emacs est une success story de ce genre de choses. Je préfère cela au système de plug-in de style Eclipse, car si je veux étendre les fonctionnalités, je n'ai pas à apprendre l'API ni à écrire/compiler un plug-in séparé. Je peux écrire un extrait de 3 lignes dans le tampon actuel, l’évaluer et l’utiliser. Courbe d'apprentissage très lisse et des résultats très agréables.
Une application que j'ai un peu étendue est Trac . Il a une architecture de composant qui dans cette situation signifie que les tâches sont déléguées à des modules qui annoncent des points d’extension. Vous pouvez ensuite implémenter d'autres composants qui s'inséreraient dans ces points et modifieraient le flux. C'est un peu la suggestion de Kalkie ci-dessus.
Un autre qui est bon est py.test . Il suit la philosophie "la meilleure API n'est pas une API" et repose uniquement sur l'appel des hooks à tous les niveaux. Vous pouvez remplacer ces points d'ancrage dans les fichiers/fonctions nommés conformément à une convention et modifier le comportement. Vous pouvez voir la liste des plugins sur le site pour voir avec quelle rapidité/facilité ils peuvent être implémentés.
Quelques points généraux.
D'après mon expérience, il existe vraiment deux types d'architectures de plug-in.
Eclipse model
qui est destiné à permettre la liberté et est ouvert.narrow API
_ parce que le plugin remplira une fonction spécifique.Pour le dire différemment, l’un permet aux plugins d’accéder à votre application tandis que l’autre permet à votre application d’accéder à plugins .
La distinction est subtile, et parfois il n'y a pas de distinction ... vous voulez les deux pour votre application.
Je n'ai pas une tonne d'expérience avec Eclipse/Ouverture de votre application au modèle de plugins (l'article dans l'article de Kalkie est excellent). J'ai lu un peu sur la façon dont Eclipse fait les choses, mais rien de plus.
Yegge blog de propriétés explique un peu comment l'utilisation du modèle de propriétés permet des plugins et de l'extensibilité.
La plupart de mes travaux ont utilisé une architecture de plug-in pour permettre à mon application d'accéder à des plug-ins, par exemple des données de temps/affichage/carte, etc.
Il y a des années, je créais des usines, des gestionnaires de plug-ins et des fichiers de configuration pour gérer tout cela et me laissais déterminer quel plug-in utiliser au moment de l'exécution.
DI framework
fais la plupart de ce travail.Je dois encore écrire des adaptateurs pour utiliser des bibliothèques tierces, mais elles ne sont généralement pas si mauvaises.
L'une des meilleures architectures de plug-in que j'ai vu est implémentée dans Eclipse. Au lieu d'avoir une application avec un modèle de plug-in, tout est un plug-in. L'application de base elle-même est le framework de plug-in.
http://www.Eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
J'ai déjà travaillé sur un projet qui devait être si flexible dans la manière dont chaque client pouvait configurer le système. Le seul bon concept que nous ayons trouvé consistait à lui envoyer un compilateur C #!
Si la spécification est remplie avec des mots tels que:
Posez beaucoup de questions sur la manière dont vous allez soutenir le système (et comment le support sera facturé, chaque client pensant que son cas est le cas normal et qu'il ne devrait pas avoir besoin de plug-ins.), Comme dans mon expérience
Le support des clients (ou des responsables du support technique) qui écrivent des plug-ins est beaucoup plus difficile que l'architecture.
Je vais décrire une technique assez simple que j'ai utilisée dans le passé. Cette approche utilise la réflexion C # pour faciliter le processus de chargement du plugin. Cette technique peut être modifiée pour s'appliquer au C++, mais vous perdez le confort de pouvoir utiliser la réflexion.
Une interface IPlugin
est utilisée pour identifier les classes qui implémentent des plugins. Des méthodes sont ajoutées à l'interface pour permettre à l'application de communiquer avec le plugin. Par exemple, la méthode Init
que l'application utilisera pour demander au plug-in de s'initialiser.
Pour rechercher des plugins, l'application analyse un dossier de plugins pour les assemblages .Net. Chaque assemblée est chargée. Reflection est utilisé pour rechercher les classes qui implémentent IPlugin
. Une instance de chaque classe de plugin est créée.
(Un fichier XML peut également répertorier les assemblys et les classes à charger. Cela peut améliorer les performances mais je n'ai jamais trouvé de problème avec les performances).
La méthode Init
est appelée pour chaque objet du plugin. Une référence à un objet qui implémente l'interface de l'application est transmise: IApplication
(ou un autre nom spécifique de votre application, par exemple ITextEditorApplication).
IApplication
contient des méthodes permettant au plugin de communiquer avec l'application. Par exemple, si vous écrivez un éditeur de texte, cette interface aurait une propriété OpenDocuments
qui permet aux plugins d’énumérer la collection de documents actuellement ouverts.
Ce système de plug-in peut être étendu aux langages de script, par exemple Lua, en créant une classe de plug-in dérivée, par exemple LuaPlugin
, qui transmet les fonctions IPlugin
et l'interface de l'application à un script Lua.
Cette technique vous permet d'implémenter de manière itérative votre IPlugin
, IApplication
et d'autres interfaces spécifiques à l'application au cours du développement. Lorsque l'application est terminée et bien refactorisée, vous pouvez documenter vos interfaces exposées et vous devez disposer d'un système Nice pour lequel les utilisateurs peuvent écrire leurs propres plugins.
D'habitude j'utilise MEF. Le cadre d'extensibilité géré (ou MEF) simplifie la création d'applications extensibles. MEF offre des fonctionnalités de découverte et de composition que vous pouvez utiliser pour charger des extensions d'application.
Si vous êtes intéressé, lisez plus ...
D'après mon expérience, les langages de script et les bibliothèques sont les deux meilleurs moyens de créer une architecture de plug-in flexible. Ces deux concepts sont dans mon esprit orthogonaux; les deux peuvent être mélangés dans n'importe quelle proportion, plutôt que la programmation fonctionnelle et orientée objet, mais trouvent leurs plus grandes forces lorsqu'ils sont équilibrés. Une bibliothèque est généralement responsable de l'exécution d'une interface spécifique avec une fonctionnalité dynamique, alors que les scripts ont tendance à mettre l'accent sur fonctionnalité avec une interface dynamique.
J'ai trouvé qu'une architecture basée sur scripts gérant des bibliothèques semble fonctionner le mieux. Le langage de script permet une manipulation de haut niveau des bibliothèques de niveau inférieur. Les bibliothèques sont ainsi libérées de toute interface spécifique, laissant ainsi toute l’interaction au niveau de l’application entre les mains plus souples du système de script.
Pour que cela fonctionne, le système de script doit avoir une API assez robuste, avec des liaisons aux données de l'application, à la logique et à l'interface graphique, ainsi qu'aux fonctionnalités de base pour importer et exécuter du code à partir de bibliothèques. En outre, les scripts doivent généralement être sûr dans le sens où l’application peut récupérer avec élégance à partir d’un script mal écrit. L'utilisation d'un système de script comme couche d'indirection signifie que l'application peut se détacher plus facilement en cas de Something Bad ™.
Le moyen de packaging des plugins dépend en grande partie de vos préférences personnelles, mais vous ne pouvez jamais vous tromper avec une archive compressée avec une interface simple, disons PluginName.ext
dans le répertoire racine.
Je pense que vous devez d’abord répondre à la question: "Quels composants sont censés être des plugins?" Vous voulez garder ce nombre au minimum absolu ou le nombre de combinaisons que vous devez tester explose. Essayez de séparer votre produit principal (qui ne devrait pas avoir trop de flexibilité) de la fonctionnalité du plug-in.
J'ai constaté que le principal IOC (Inversion of Control)) (lire Springframework) fonctionne bien pour fournir une base flexible, à laquelle vous pouvez ajouter une spécialisation pour faciliter le développement de plug-in.