Powtarzającym się tematem w moich pracach programistycznych było wykorzystanie lub stworzenie własnej architektury wtyczek. Widziałem, że podchodzono do tego na wiele sposobów - pliki konfiguracyjne (XML, .conf itd.), Struktury dziedziczenia, informacje o bazach danych, biblioteki i inne. Z mojego doświadczenia:
- Baza danych nie jest dobrym miejscem do przechowywania informacji o konfiguracji, zwłaszcza w połączeniu z danymi
- Próba tego z hierarchią dziedziczenia wymaga wiedzy na temat wtyczek do zakodowania, co oznacza, że architektura wtyczek nie jest aż tak dynamiczna
- Pliki konfiguracyjne dobrze sprawdzają się w dostarczaniu prostych informacji, ale nie obsługują bardziej złożonych zachowań
- Wydaje się, że biblioteki działają dobrze, ale zależności jednokierunkowe muszą być starannie tworzone.
Kiedy staram się uczyć z różnych architektur, z którymi pracowałem, szukam także sugestii społeczności. W jaki sposób zaimplementowałeś architekturę wtyczek SOLID? Jaka była Twoja najgorsza porażka (lub najgorsza porażka, jaką widziałeś)? Co byś zrobił, gdybyś miał zaimplementować nową architekturę wtyczek? Który projekt SDK lub open source, z którym pracowałeś, ma najlepszy przykład dobrej architektury?
Kilka przykładów, które sam znalazłem:
- Perl's Module :: Plugable i IOC do iniekcji zależności w Perlu
- Różne struktury Spring (Java, .NET, Python) do wstrzykiwania zależności.
- Pytanie SO z listą for Java (łącznie z interfejsami Service Provider )
- Pytanie SO dla C ++ wskazując na artykuł Dr. Dobbs
- SO pytanie dotyczące pomysłu specyficzny plugin dla ASP.NET MVC
Te przykłady wydają się grać dla różnych mocnych stron języka. Czy dobra architektura wtyczki jest koniecznie powiązana z językiem? Czy najlepiej jest użyć narzędzi do tworzenia architektury wtyczek, czy zrobić to na własnych, następujących modelach?
Odpowiedzi:
To nie jest odpowiedź, a garść potencjalnie przydatnych uwag / przykładów.
Jednym ze skutecznych sposobów na rozszerzenie aplikacji jest ujawnienie jej elementów wewnętrznych jako języka skryptowego i napisanie wszystkich najważniejszych rzeczy w tym języku. To sprawia, że jest dość modyfikowalny i praktycznie przyszłościowy (jeśli twoje prymitywy są dobrze wybrane i zaimplementowane). Historią sukcesu tego rodzaju rzeczy jest Emacs. Wolę to od systemu wtyczek w stylu eclipse, ponieważ jeśli chcę rozszerzyć funkcjonalność, nie muszę uczyć się API i pisać / kompilować osobnej wtyczki. Potrafię napisać 3-liniowy fragment w samym bieżącym buforze, ocenić go i użyć. Bardzo płynna krzywa uczenia się i bardzo przyjemne wyniki.
Jedną z aplikacji, którą trochę rozszerzyłem, jest Trac . Ma architekturę komponentów, co w tej sytuacji oznacza, że zadania są delegowane do modułów ogłaszających punkty rozszerzeń. Następnie możesz zaimplementować inne komponenty, które pasowałyby do tych punktów i zmienić przepływ. To trochę jak sugestia Kalkie powyżej.
Kolejnym dobrym jest py.test . Jest zgodne z filozofią „best API is no API” i opiera się wyłącznie na wywoływaniu hooków na każdym poziomie. Możesz przesłonić te punkty zaczepienia w plikach / funkcjach nazwanych zgodnie z konwencją i zmienić zachowanie. Możesz zobaczyć listę wtyczek na stronie, aby zobaczyć, jak szybko / łatwo można je zaimplementować.
Kilka ogólnych uwag.
źródło
Z mojego doświadczenia wynika, że istnieją naprawdę dwa typy architektur wtyczek.
Eclipse model
co ma pozwolić na wolność i jest otwarty.narrow API
po znaku, ponieważ wtyczka będzie wypełniać określoną funkcję.Aby wyrazić to w inny sposób, jedna umożliwia wtyczkom dostęp do aplikacji, a druga umożliwia aplikacji dostęp do wtyczek .
Rozróżnienie jest subtelne i czasami nie ma rozróżnienia ... potrzebujesz obu do swojego zastosowania.
Nie mam dużego doświadczenia z Eclipse / Otwieraniem twojej aplikacji na model wtyczek (artykuł w poście Kalkie jest świetny). Czytałem trochę o tym, jak zaćmienie działa, ale nic poza tym.
Blog właściwości Yegge mówi trochę o tym, jak użycie wzorca właściwości pozwala na wtyczki i rozszerzalność.
Większość pracy, którą wykonałem, wykorzystywała architekturę wtyczek, aby umożliwić mojej aplikacji dostęp do wtyczek, rzeczy takich jak dane dotyczące czasu / wyświetlania / mapy itp.
Wiele lat temu tworzyłem fabryki, menedżery wtyczek i pliki konfiguracyjne, aby zarządzać tym wszystkim i pozwolić mi określić, której wtyczki użyć w czasie wykonywania.
DI framework
wykonuję większość tej pracy.Nadal muszę pisać adaptery, aby korzystać z bibliotek innych firm, ale zwykle nie są takie złe.
źródło
Jedna z najlepszych architektur wtyczek, jakie widziałem, została zaimplementowana w Eclipse. Zamiast mieć aplikację z modelem wtyczki, wszystko jest wtyczką. Podstawową aplikacją jest sama struktura wtyczek.
http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
źródło
Opiszę dość prostą technikę, której używałem w przeszłości. To podejście wykorzystuje odbicie C #, aby pomóc w procesie ładowania wtyczki. Tę technikę można zmodyfikować, aby można ją było zastosować w C ++, ale tracisz wygodę korzystania z odbicia.
IPlugin
Interfejs służy do identyfikacji klasy implementujące wtyczek. Do interfejsu dodano metody umożliwiające komunikację aplikacji z wtyczką. Na przykładInit
metoda, której aplikacja będzie używać do instruowania wtyczki, aby zainicjowała.Aby znaleźć wtyczki, aplikacja skanuje folder wtyczek w poszukiwaniu zestawów .Net. Każdy zespół jest ładowany. Odbicie służy do skanowania klas, które implementują
IPlugin
. Tworzona jest instancja każdej klasy wtyczki.(Alternatywnie, plik XML może zawierać listę zestawów i klas do załadowania. Może to zwiększyć wydajność, ale nigdy nie znalazłem problemu z wydajnością).
Init
Metoda jest wywoływana dla każdego obiektu wtyczek. Jest przekazywana referencja do obiektu, który implementuje interfejs aplikacji:IApplication
(lub coś innego o nazwie specyficznej dla Twojej aplikacji, np. ITextEditorApplication).IApplication
zawiera metody umożliwiające wtyczce komunikację z aplikacją. Na przykład, jeśli piszesz edytor tekstu, ten interfejs miałbyOpenDocuments
właściwość, która umożliwia wtyczkom wyliczanie kolekcji aktualnie otwartych dokumentów.Ten system wtyczek można rozszerzyć o języki skryptowe, np. Lua, tworząc pochodną klasę wtyczki, np.
LuaPlugin
PrzekazującąIPlugin
funkcje i interfejs aplikacji do skryptu Lua.Technika ta pozwala na iteracyjnie realizować swoje
IPlugin
,IApplication
i inne interfejsy specyficzne dla aplikacji w trakcie rozwoju. Kiedy aplikacja jest kompletna i ładnie zreformowana, możesz udokumentować swoje ujawnione interfejsy i powinieneś mieć fajny system, dla którego użytkownicy mogą pisać własne wtyczki.źródło
Zwykle używam MEF. Managed Extensibility Framework (lub w skrócie MEF) upraszcza tworzenie rozszerzalnych aplikacji. MEF oferuje funkcje wykrywania i kompozycji, które można wykorzystać do ładowania rozszerzeń aplikacji.
Jeśli jesteś zainteresowany, przeczytaj więcej ...
źródło
Kiedyś pracowałem nad projektem, który musiał być tak elastyczny pod względem sposobu, w jaki każdy klient mógł skonfigurować system, a jedynym dobrym projektem, jaki znaleźliśmy, było dostarczenie klientowi kompilatora C #!
Jeśli specyfikacja jest wypełniona słowami takimi jak:
Zadawaj wiele pytań o to, w jaki sposób będziesz obsługiwać system (i jak będzie naliczana opłata za wsparcie, ponieważ każdy klient będzie uważał, że jego sprawa jest normalna i nie potrzebuje żadnych wtyczek), tak jak z mojego doświadczenia.
źródło
Z mojego doświadczenia wynika, że dwa najlepsze sposoby na stworzenie elastycznej architektury wtyczek to języki skryptowe i biblioteki. Te dwie koncepcje są w moim umyśle ortogonalne; można je mieszać w dowolnych proporcjach, podobnie jak programowanie funkcjonalne i obiektowe, ale ich największe zalety znajdują się w równowadze. Biblioteka jest zwykle odpowiedzialna za wypełnienie określonego interfejsu dynamiczną funkcjonalnością, podczas gdy skrypty mają tendencję do kładzenia nacisku na funkcjonalność za pomocą dynamicznego interfejsu.
Odkryłem, że najlepiej sprawdza się architektura oparta na skryptach zarządzających bibliotekami . Język skryptowy umożliwia wysokopoziomową manipulację bibliotekami niższego poziomu, dzięki czemu biblioteki są zwolnione z jakiegokolwiek określonego interfejsu, pozostawiając całą interakcję na poziomie aplikacji w bardziej elastycznych rękach systemu skryptowego.
Aby to działało, system skryptów musi mieć dość solidne API, z zaczepami do danych aplikacji, logiki i GUI, a także podstawową funkcjonalność importu i wykonywania kodu z bibliotek. Co więcej, skrypty są zwykle wymagane, aby były bezpieczne w tym sensie, że aplikacja może z wdziękiem odzyskać dane po źle napisanym skrypcie. Używanie systemu skryptów jako warstwy pośredniej oznacza, że aplikacja może łatwiej odłączyć się w przypadku wystąpienia czegoś złego ™.
Sposób pakowania wtyczek zależy w dużej mierze od osobistych preferencji, ale nigdy nie można się pomylić ze skompresowanym archiwum z prostym interfejsem, powiedzmy
PluginName.ext
w katalogu głównym.źródło
Myślę, że najpierw musisz odpowiedzieć na pytanie: „Jakie komponenty mają być wtyczkami?” Chcesz ograniczyć tę liczbę do absolutnego minimum lub liczbę kombinacji, które musisz przetestować, wybucha. Spróbuj oddzielić swój podstawowy produkt (który nie powinien mieć zbyt dużej elastyczności) od funkcjonalności wtyczki.
Odkryłem, że zasada IOC (Inversion of Control) (przeczytaj springframework) działa dobrze, zapewniając elastyczną bazę, do której możesz dodać specjalizację, aby uprościć tworzenie wtyczek.
źródło
Wzorzec wtyczki to programowy wzorzec służący do rozszerzania zachowania klasy o przejrzysty interfejs. Często zachowanie klas jest rozszerzone przez dziedziczenie klas, gdzie klasa pochodna nadpisuje niektóre metody wirtualne klasy. Problem z tym rozwiązaniem polega na tym, że koliduje z ukrywaniem implementacji. Prowadzi to również do sytuacji, w których klasa pochodna staje się miejscem gromadzenia niepowiązanych rozszerzeń zachowania. Również skrypty są używane do implementacji tego wzorca, jak wspomniano powyżej "Twórz elementy wewnętrzne jako język skryptowy i pisz wszystkie rzeczy najwyższego poziomu w tym języku. To sprawia, że jest on dość modyfikowalny i praktycznie zabezpieczony w przyszłości". Biblioteki używają bibliotek zarządzających skryptami. Język skryptowy umożliwia wysokopoziomową manipulację bibliotekami niższego poziomu. (Również jak wspomniano powyżej)
źródło