it-swarm.com.de

So erstellen Sie eine flexible Plug-In-Architektur

Ein sich wiederholendes Thema in meiner Entwicklungsarbeit war die Verwendung oder Erstellung einer internen Plug-In-Architektur. Ich habe gesehen, dass es viele Ansätze gibt - 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
  • Wenn Sie dies mit einer Vererbungshierarchie versuchen, müssen Sie über Kenntnisse der zu codierenden Plug-Ins verfügen, sodass 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 Anregungen. Wie haben Sie eine SOLID Plug-In-Architektur implementiert? Was war Ihr schlimmster Fehler (oder der schlimmste Fehler, den Sie je gesehen haben)? Was würden Sie tun, wenn Sie ein neues Plug-In implementieren würden? In der Architektur? Welches SDK oder Open Source-Projekt, mit dem Sie gearbeitet haben, hat das beste Beispiel für eine gute Architektur?

Einige Beispiele, die ich selbst gefunden habe:

Diese Beispiele scheinen verschiedene Sprachstärken zu spielen. Ist eine gute Plugin-Architektur unbedingt an die Sprache gebunden? Ist es am besten, Tools zu verwenden, um eine Plug-in-Architektur zu erstellen, oder es auf eigenen folgenden Modellen zu tun?

148
justkt

Dies ist keine Antwort , sondern eine Reihe potenziell nützlicher Bemerkungen/Beispiele.

  • Eine effektive Möglichkeit, Ihre Anwendung erweiterbar zu machen, besteht darin, ihre Interna als Skriptsprache verfügbar zu machen und alle wichtigen Informationen in dieser Sprache zu verfassen. Dies macht es sehr anpassbar und praktisch zukunftssicher (wenn Ihre Grundelemente gut ausgewählt und implementiert sind). Eine Erfolgsgeschichte dieser Art ist Emacs. Ich bevorzuge dies dem Eclipse-Plugin-System, 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, auswerten und verwenden. Sehr flüssige Lernkurve und sehr erfreuliche Ergebnisse.

  • Eine Anwendung, die ich ein wenig erweitert habe, ist Trac . Es verfügt über eine Komponentenarchitektur, die in diesem Fall 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.

  • Ein anderes, was gut ist, ist py.test . Es folgt der "Best API is no API" -Philosophie und beruht ausschließlich darauf, dass Hooks auf jeder Ebene aufgerufen werden. Sie können diese Hooks in Dateien/Funktionen überschreiben, die gemäß einer Konvention benannt wurden, und das Verhalten ändern. Sie können die Liste der Plugins auf der Website sehen, um zu sehen, wie schnell/einfach sie implementiert werden können.

Ein paar allgemeine Punkte.

  • Versuchen Sie, Ihren nicht erweiterbaren/nicht vom Benutzer veränderbaren Kern so klein wie möglich zu halten. Delegieren Sie alles, was Sie können, auf eine höhere Ebene, damit die Erweiterbarkeit erhöht wird. Weniger Dinge im Kern zu korrigieren, als bei schlechten Entscheidungen.
  • Im Zusammenhang mit dem obigen Punkt sollten Sie zu Beginn nicht zu viele Entscheidungen über die Richtung Ihres Projekts treffen. Implementieren Sie die kleinste benötigte Teilmenge und schreiben Sie dann Plugins.
  • Wenn Sie eine Skriptsprache einbetten, stellen Sie sicher, dass es eine vollständige Sprache ist, in der Sie allgemeine Programme schreiben können und keine Spielzeugsprache , nur für Ihre Anwendung.
  • Reduzieren Sie die Heizplatte so weit wie möglich. Kümmere dich nicht um Unterklassen, komplexe APIs, Plugin-Registrierung und ähnliches. Versuchen Sie es einfach zu halten, damit es einfach und nicht nur möglich ist erweitern. Dadurch kann Ihre Plugin-API besser genutzt werden und Endbenutzer können Plugins schreiben. Nicht nur Plugin-Entwickler. py.test macht das gut. Eclipse soweit ich weiß, nicht .
81
Noufal Ibrahim

Nach meiner Erfahrung gibt es zwei Arten von Plug-In-Architekturen.

  1. Man folgt dem Eclipse model, Der Freiheit ermöglichen soll und unbefristet ist.
  2. Das andere erfordert normalerweise, dass Plugins einem narrow API Folgen, da das Plugin eine bestimmte Funktion ausfüllt.

Um dies anders auszudrücken, ermöglicht ein Plugin den Zugriff auf Ihre Anwendung , während das andere Plugin den Zugriff auf Ihre Anwendung ermöglicht Plugins .

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 Sie Ihre App für Plugins-Modell (der Artikel in Kalkies Post ist großartig). Ich habe etwas darüber gelesen, wie Eclipse Dinge tut, aber nichts weiter.

Yegges Eigenschaften-Blog spricht ein wenig darüber, wie die Verwendung des Eigenschaften-Musters Plugins und Erweiterbarkeit ermöglicht.

Bei den meisten meiner Arbeiten wurde eine Plugin-Architektur verwendet, um meiner App den Zugriff auf Plugins, z. B. Zeit-/Anzeige-/Kartendaten usw. zu ermöglichen.

Vor Jahren habe ich Fabriken, Plug-in-Manager und Konfigurationsdateien erstellt, um all das zu verwalten, und ich habe festgelegt, welches Plug-in zur Laufzeit verwendet werden soll.

  • Jetzt muss ich normalerweise nur noch einen DI framework Erledigen.

Ich muss noch Adapter schreiben, um Bibliotheken von Drittanbietern zu verwenden, aber normalerweise sind sie nicht so schlecht.

24
derivation

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

14
Patrick

Ich habe einmal an einem Projekt gearbeitet, das so flexibel sein musste, dass jeder Kunde das System einrichten konnte. Das einzige gute Design, das wir fanden, war, dem Kunden einen C # -Compiler zu schicken!

Wenn die Spezifikation mit folgenden Wörtern gefüllt ist:

  • Flexibel
  • Plug-In
  • Anpassbare

Stellen Sie viele Fragen dazu, wie Sie das System unterstützen (und wie der Support in Rechnung gestellt wird, da jeder Kunde der Meinung ist, dass sein Fall der Normalfall ist und keine Plug-Ins benötigen sollte.), Wie ich es erlebt habe

Die Unterstützung von Kunden (oder Supportmitarbeitern), die Plug-Ins schreiben, ist viel schwieriger als die Architektur

8
Ian Ringrose

Ich beschreibe eine ziemlich einfache Technik, die ich in der Vergangenheit verwendet habe. Bei diesem Ansatz wird C # -Reflexion verwendet, um den Ladevorgang des Plugins zu erleichtern. Diese Technik kann so modifiziert werden, dass sie auf C++ anwendbar ist, Sie verlieren jedoch den Komfort, Reflexion 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 die Init -Methode, die die Anwendung verwendet, um das Plugin zur Initialisierung anzuweisen.

Um Plugins zu finden, durchsucht die Anwendung einen Plugin-Ordner nach .Net-Assemblys. Jede Assembly wird geladen. Reflection wird verwendet, um nach Klassen zu suchen, die IPlugin implementieren. Eine Instanz jeder Plugin-Klasse wird 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 Methode Init 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 Anwendung spezifisch benannt 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 die Eigenschaft OpenDocuments, mit der Plug-ins die Auflistung der aktuell geöffneten Dokumente auflisten können.

Dieses Plugin-System kann durch Erstellen einer abgeleiteten Plugin-Klasse, z. B. LuaPlugin, die IPlugin Funktionen und die Anwendungsschnittstelle an ein Lua-Skript weiterleitet, auf Skriptsprachen, z. B. Lua, erweitert werden.

Mit dieser Technik können Sie Ihre IPlugin, IApplication und andere anwendungsspezifische Schnittstellen während der Entwicklung iterativ implementieren. Wenn die Anwendung vollständig und gut überarbeitet ist, können Sie Ihre exponierten Schnittstellen dokumentieren und Sie sollten ein Nice-System haben, für das Benutzer ihre eigenen Plugins schreiben können.

7
Ashley Davis

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 ...

7
BALKANGraph

Nach meiner Erfahrung sind Skriptsprachen und Bibliotheken die beiden besten Möglichkeiten, eine flexible Plugin-Architektur zu erstellen. Diese beiden Konzepte sind in meinem Kopf orthogonal; Die beiden können in jedem Verhältnis gemischt werden, ähnlich wie funktionale und objektorientierte Programmierung, aber ihre größten Stärken finden sie, wenn sie ausgewogen sind. Eine Bibliothek ist in der Regel dafür verantwortlich, eine bestimmte Schnittstelle mit dynamischer Funktionalität zu erfüllen, wohingegen Skripte Funktionalität mit dynamischer Schnittstelle in der Regel hervorheben.

Ich habe festgestellt, dass eine Architektur, die auf Skripten, die Bibliotheken verwalten basiert, am besten zu funktionieren scheint. Die Skriptsprache ermöglicht die Manipulation niedrigerer Bibliotheken auf hoher Ebene, und die Bibliotheken sind somit von jeder spezifischen Schnittstelle befreit, so dass die gesamte Interaktion auf Anwendungsebene in den flexibleren Händen des Skriptsystems bleibt.

Damit dies funktioniert, muss das Skriptsystem über eine recht robuste API verfügen, die Hooks für Anwendungsdaten, Logik und GUI sowie die Basisfunktionalität zum Importieren und Ausführen von Code aus Bibliotheken enthält. Außerdem müssen Skripte normalerweise sicher sein, damit die Anwendung ordnungsgemäß von einem schlecht geschriebenen Skript wiederhergestellt werden kann. Durch die Verwendung eines Skriptsystems als Indirektionsebene kann sich die Anwendung im Fall von Something Bad ™ leichter von selbst trennen.

Die Art und Weise, wie Plugins verpackt werden, hängt weitgehend von Ihren persönlichen Vorlieben ab. Mit einem komprimierten Archiv können Sie jedoch niemals mit einer einfachen Schnittstelle etwas falsch machen, z. B. PluginName.ext im Stammverzeichnis.

3
Jon Purdy

Ich denke, Sie müssen zuerst die Frage beantworten: "Welche Komponenten sollen Plugins sein?" Sie möchten diese Anzahl auf ein absolutes Minimum beschränken oder die Anzahl der Kombinationen, die Sie testen müssen, explodieren lassen. Versuchen Sie, Ihr Kernprodukt (das nicht zu flexibel sein sollte) von der Plug-in-Funktionalität zu trennen.

Ich habe festgestellt, dass das Prinzip IOC (Inversion of Control)) (read springframework) gut funktioniert, um eine flexible Basis bereitzustellen, die Sie durch Spezialisierung vereinfachen können.

  • Sie können den Container nach dem Mechanismus "Schnittstelle als Werbung vom Typ Plugin" durchsuchen.
  • Sie können den Container verwenden, um allgemeine Abhängigkeiten einzufügen, die für Plugins erforderlich sind (z. B. ResourceLoaderAware oder MessageSourceAware).
3
Justin