Ein sich wiederholendes Thema in meiner Entwicklungsarbeit war die Verwendung oder Erstellung einer internen Plug-In-Architektur. Ich habe gesehen, wie es auf viele Arten angegangen wurde - Konfigurationsdateien (XML, .conf usw.), Vererbungsframeworks, Datenbankinformationen, Bibliotheken und andere. Durch meine Erfahrung:
- Eine Datenbank ist kein großartiger Ort, um Ihre Konfigurationsinformationen zu speichern, insbesondere wenn sie mit Daten vermischt sind
- Um dies mit einer Vererbungshierarchie zu versuchen, sind Kenntnisse über die zu codierenden Plug-Ins erforderlich, was bedeutet, dass die Plug-In-Architektur nicht allzu dynamisch ist
- Konfigurationsdateien eignen sich gut für die Bereitstellung einfacher Informationen, können jedoch nicht mit komplexeren Verhaltensweisen umgehen
- Bibliotheken scheinen gut zu funktionieren, aber die Einwegabhängigkeiten müssen sorgfältig erstellt werden.
Da ich von den verschiedenen Architekturen lernen möchte, mit denen ich gearbeitet habe, suche ich auch bei der Community nach Vorschlägen. Wie haben Sie eine SOLID-Plug-In-Architektur implementiert? Was war Ihr schlimmster Fehler (oder der schlimmste Fehler, den Sie gesehen haben)? Was würden Sie tun, wenn Sie eine neue Plug-In-Architektur implementieren würden? Welches SDK oder Open Source-Projekt, mit dem Sie gearbeitet haben, ist das beste Beispiel für eine gute Architektur?
Einige Beispiele, die ich selbst gefunden habe:
- Perls Modul :: Plugable und IOC für die Abhängigkeitsinjektion in Perl
- Die verschiedenen Spring-Frameworks (Java, .NET, Python) für die Abhängigkeitsinjektion.
- Eine SO-Frage mit einer Liste für Java (einschließlich Service Provider-Schnittstellen )
- Eine SO-Frage für C ++, die auf einen Artikel von Dr. Dobbs verweist
- Eine SO-Frage zu einer bestimmten Plugin-Idee für ASP.NET MVC
Diese Beispiele scheinen mit verschiedenen Sprachstärken zu spielen. Ist eine gute Plugin-Architektur unbedingt an die Sprache gebunden? Ist es am besten, Tools zu verwenden, um eine Plugin-Architektur zu erstellen, oder dies auf eigenen folgenden Modellen zu tun?
Antworten:
Dies ist weniger eine Antwort als eine Reihe potenziell nützlicher Bemerkungen / Beispiele.
Eine effektive Möglichkeit, Ihre Anwendung erweiterbar zu machen, besteht darin, die Interna als Skriptsprache verfügbar zu machen und alle Inhalte der obersten Ebene in dieser Sprache zu schreiben. Dies macht es ziemlich modifizierbar und praktisch zukunftssicher (wenn Ihre Grundelemente gut ausgewählt und implementiert sind). Eine Erfolgsgeschichte dieser Art ist Emacs. Ich ziehe dies dem Plugin-System im Eclipse-Stil vor, da ich, wenn ich die Funktionalität erweitern möchte, die API nicht lernen und kein separates Plugin schreiben / kompilieren muss. Ich kann ein 3-Zeilen-Snippet in den aktuellen Puffer selbst schreiben, es auswerten und verwenden. Sehr reibungslose Lernkurve und sehr erfreuliche Ergebnisse.
Eine Anwendung, die ich ein wenig erweitert habe, ist Trac . Es verfügt über eine Komponentenarchitektur, die in dieser Situation bedeutet, dass Aufgaben an Module delegiert werden, die Erweiterungspunkte ankündigen. Sie können dann andere Komponenten implementieren, die in diese Punkte passen und den Fluss ändern. Es ist ein bisschen wie Kalkies Vorschlag oben.
Eine andere, die gut ist, ist py.test . Es folgt der Philosophie "Beste API ist keine API" und basiert ausschließlich auf Hooks, die auf jeder Ebene aufgerufen werden. Sie können diese Hooks in Dateien / Funktionen überschreiben, die gemäß einer Konvention benannt sind, und das Verhalten ändern. Sie können die Liste der Plugins auf der Site sehen, um zu sehen, wie schnell / einfach sie implementiert werden können.
Ein paar allgemeine Punkte.
quelle
Nach meiner Erfahrung gibt es zwei Arten von Plug-In-Architekturen.
Eclipse model
was Freiheit ermöglichen soll und offen ist.narrow API
da das Plugin eine bestimmte Funktion erfüllt.Um dies anders auszudrücken, ermöglicht eines Plugins den Zugriff auf Ihre Anwendung, während das andere Ihrer Anwendung den Zugriff auf Plugins ermöglicht .
Die Unterscheidung ist subtil und manchmal gibt es keine Unterscheidung ... Sie möchten beides für Ihre Anwendung.
Ich habe nicht viel Erfahrung mit Eclipse / Öffnen Ihrer App für Plugins-Modell (der Artikel in Kalkies Beitrag ist großartig). Ich habe ein bisschen darüber gelesen, wie Eclipse Dinge tut, aber nichts weiter.
In Yegges Eigenschaftenblog wird ein wenig darüber gesprochen, wie die Verwendung des Eigenschaftenmusters Plugins und Erweiterbarkeit ermöglicht.
Die meiste Arbeit, die ich geleistet habe, hat eine Plugin-Architektur verwendet, um meiner App den Zugriff auf Plugins zu ermöglichen, z. B. Zeit- / Anzeige- / Kartendaten usw.
Vor Jahren habe ich Fabriken, Plugin-Manager und Konfigurationsdateien erstellt, um alles zu verwalten, und mich bestimmen lassen, welches Plugin zur Laufzeit verwendet werden soll.
DI framework
größten Teil dieser Arbeit erledigen.Ich muss noch Adapter schreiben, um Bibliotheken von Drittanbietern zu verwenden, aber sie sind normalerweise nicht so schlecht.
quelle
Eine der besten Plug-In-Architekturen, die ich je gesehen habe, ist in Eclipse implementiert. Anstatt eine Anwendung mit einem Plug-In-Modell zu haben, ist alles ein Plug-In. Die Basisanwendung selbst ist das Plug-In-Framework.
http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
quelle
Ich werde eine ziemlich einfache Technik beschreiben, die ich in der Vergangenheit verwendet habe. Dieser Ansatz verwendet C # -Reflexion, um den Ladevorgang des Plugins zu unterstützen. Diese Technik kann so geändert werden, dass sie auf C ++ anwendbar ist. Sie verlieren jedoch den Komfort, Reflektion verwenden zu können.
Eine
IPlugin
Schnittstelle wird verwendet, um Klassen zu identifizieren, die Plugins implementieren. Der Schnittstelle werden Methoden hinzugefügt, damit die Anwendung mit dem Plugin kommunizieren kann. Zum Beispiel dieInit
Methode, mit der die Anwendung das Plugin zum Initialisieren anweist.Um Plugins zu finden, durchsucht die Anwendung einen Plugin-Ordner nach .NET-Assemblys. Jede Baugruppe wird geladen. Reflection wird verwendet, um nach implementierten Klassen zu suchen
IPlugin
. Für jede Plugin-Klasse wird eine Instanz erstellt.(Alternativ kann eine XML-Datei die zu ladenden Assemblys und Klassen auflisten. Dies kann die Leistung verbessern, aber ich habe nie ein Problem mit der Leistung gefunden.)
Die
Init
Methode wird für jedes Plugin-Objekt aufgerufen. Es wird ein Verweis auf ein Objekt übergeben, das die Anwendungsschnittstelle implementiert:IApplication
(oder etwas anderes, das für Ihre App spezifisch ist, z. B. ITextEditorApplication).IApplication
enthält Methoden, mit denen das Plugin mit der Anwendung kommunizieren kann. Wenn Sie beispielsweise einen Texteditor schreiben, verfügt diese Schnittstelle über eineOpenDocuments
Eigenschaft, mit der Plugins die Sammlung aktuell geöffneter Dokumente auflisten können.Dieses Plugin-System kann auf Skriptsprachen, z. B. Lua, erweitert werden, indem eine abgeleitete Plugin-Klasse erstellt wird, z. B.
LuaPlugin
dieIPlugin
Funktionen und die Anwendungsschnittstelle an ein Lua-Skript weiterleitet .Diese Technik ermöglicht es Ihnen , iterativ Ihre zu implementieren
IPlugin
,IApplication
während der Entwicklung und andere anwendungsspezifische Schnittstellen. Wenn die Anwendung vollständig und gut überarbeitet ist, können Sie Ihre exponierten Schnittstellen dokumentieren und Sie sollten ein schönes System haben, für das Benutzer ihre eigenen Plugins schreiben können.quelle
Normalerweise benutze ich MEF. Das Managed Extensibility Framework (kurz MEF) vereinfacht die Erstellung erweiterbarer Anwendungen. MEF bietet Erkennungs- und Kompositionsfunktionen, mit denen Sie Anwendungserweiterungen laden können.
Wenn Sie interessiert sind, lesen Sie mehr ...
quelle
Ich habe einmal an einem Projekt gearbeitet, das so flexibel sein musste, dass jeder Kunde das System einrichten konnte. Das einzig gute Design war, dem Kunden einen C # -Compiler zu liefern!
Wenn die Spezifikation mit Wörtern wie gefüllt ist:
Stellen Sie viele Fragen dazu, wie Sie das System unterstützen (und wie der Support berechnet wird, da jeder Kunde der Meinung ist, dass sein Fall der Normalfall ist und keine Plug-Ins benötigt), wie ich es erlebt habe
quelle
Nach meiner Erfahrung sind die beiden besten Möglichkeiten zum Erstellen einer flexiblen Plugin-Architektur Skriptsprachen und Bibliotheken. Diese beiden Konzepte sind meiner Meinung nach orthogonal; Die beiden können in jedem Verhältnis gemischt werden, ähnlich wie funktionale und objektorientierte Programmierung, finden aber ihre größten Stärken, wenn sie ausgewogen sind. Eine Bibliothek ist normalerweise dafür verantwortlich, eine bestimmte Schnittstelle mit dynamischer Funktionalität zu erfüllen , während Skripte die Funktionalität mit einer dynamischen Schnittstelle hervorheben .
Ich habe festgestellt, dass eine Architektur, die auf Skripten basiert, die Bibliotheken verwalten , am besten zu funktionieren scheint. Die Skriptsprache ermöglicht die Manipulation von Bibliotheken auf niedrigerer Ebene auf hoher Ebene, sodass die Bibliotheken von jeder spezifischen Schnittstelle befreit werden und die gesamte Interaktion auf Anwendungsebene in den flexibleren Händen des Skriptsystems verbleibt.
Damit dies funktioniert, muss das Skriptsystem über eine ziemlich robuste API verfügen, die Hooks zu Anwendungsdaten, Logik und GUI sowie die Basisfunktionalität zum Importieren und Ausführen von Code aus Bibliotheken enthält. Darüber hinaus müssen Skripte normalerweise in dem Sinne sicher sein, dass die Anwendung ordnungsgemäß von einem schlecht geschriebenen Skript wiederhergestellt werden kann. Durch die Verwendung eines Skriptsystems als Indirektionsebene kann sich die Anwendung im Falle von Something Bad ™ leichter lösen.
Die Art und Weise, wie Plugins verpackt werden, hängt weitgehend von Ihren persönlichen Vorlieben ab. Mit einem komprimierten Archiv mit einer einfachen Oberfläche, beispielsweise
PluginName.ext
im Stammverzeichnis , können Sie jedoch nichts falsch machen .quelle
Ich denke, Sie müssen zuerst die Frage beantworten: "Welche Komponenten werden voraussichtlich Plugins sein?" Sie möchten diese Anzahl auf ein absolutes Minimum beschränken oder die Anzahl der Kombinationen, die Sie testen müssen, explodiert. Versuchen Sie, Ihr Kernprodukt (das nicht zu flexibel sein sollte) von der Plugin-Funktionalität zu trennen.
Ich habe festgestellt, dass das IOC-Prinzip (Inversion of Control) (read springframework) gut funktioniert, um eine flexible Basis bereitzustellen, auf die Sie sich spezialisieren können, um die Plugin-Entwicklung zu vereinfachen.
quelle
Das Plug-in-Muster ist ein Softwaremuster zum Erweitern des Verhaltens einer Klasse mit einer sauberen Schnittstelle. Oft wird das Verhalten von Klassen durch Klassenvererbung erweitert, wobei die abgeleitete Klasse einige der virtuellen Methoden der Klasse überschreibt. Ein Problem bei dieser Lösung besteht darin, dass sie mit dem Ausblenden der Implementierung in Konflikt steht. Es führt auch zu Situationen, in denen abgeleitete Klassen zu Treffpunkten nicht verwandter Verhaltenserweiterungen werden. Außerdem wird Scripting verwendet, um dieses Muster wie oben erwähnt zu implementieren. "Machen Sie Interna als Skriptsprache und schreiben Sie alle Inhalte der obersten Ebene in dieser Sprache. Dies macht es ziemlich modifizierbar und praktisch zukunftssicher." Bibliotheken verwenden Skriptverwaltungsbibliotheken. Die Skriptsprache ermöglicht die Manipulation von Bibliotheken auf niedrigerer Ebene auf hoher Ebene. (Auch wie oben erwähnt)
quelle