2015-01-17 17 views
8

W Node.js udało mi się stworzyć klan WordPressa z łatwością za pomocą EventEmitter'a, aby zreplikować i zbudować system hooks w rdzeniu CMS, do którego wtyczki mogłyby następnie dołączyć.Wydarzenia Golang: EventEmitter/dispatcher dla architektury wtyczek

Potrzebuję teraz tego samego poziomu rozszerzalności i izolacji rdzenia dla mojego CMS napisanego i przeniesionego do Go. Zasadniczo mam już gotowy rdzeń, ale aby uczynić go naprawdę elastycznym, muszę być w stanie wstawiać zdarzenia (haki) i mieć wtyczki dołączone do tych haków z dodatkową funkcjonalnością.

Nie dbam o rekompilację (łączenie dynamiczne/statyczne), o ile nie trzeba modyfikować jądra, aby załadować wtyczki - rdzeń CMS nigdy nie powinien być modyfikowany. (Jak WP, Drupal itp)

zauważyłem jest kilka raczej nieznanych projektów, starając się realizować wydarzenia w szukać nieco podobny do EventEmitter w node.js:

https://github.com/CHH/eventemitter

https://github.com/chuckpreslar/emission

Ponieważ te dwa projekty powyżej nie zyskały dużej popularności i uwagi, jakoś czuję, że ten sposób myślenia o wydarzeniach może być teraz taki, jak powinniśmy to robić w Go? Czy to znaczy, że Go może nie jest przystosowany do tego zadania? Aby tworzyć naprawdę rozszerzalne aplikacje za pomocą wtyczek?

Wygląda na to, że Go nie ma wbudowanych w jądro wydarzeń, a RPC nie wydaje się być prawidłowym rozwiązaniem do integracji wtyczek z główną aplikacją, ponieważ zostały one wbudowane natywnie i tak jakby były częścią głównej sama aplikacja.

Jaki jest najlepszy sposób na bezproblemową integrację wtyczki z podstawową aplikacją, na nieograniczone punkty rozszerzeń (w rdzeniu) bez manipulowania rdzeniem za każdym razem, gdy trzeba podłączyć nową wtyczkę?

+1

Zobacz pakiety database/sql i database/sql/driver na przykładzie architektury wtyczki. W tej architekturze wtyczki [register] (http://godoc.org/database/sql#Register) z funkcji init() i implementują interfejsy zdefiniowane przez pakiet sterowników. –

Odpowiedz

25

Ogólnie rzecz biorąc, w Go, jeśli potrzebujesz zdarzeń, prawdopodobnie potrzebujesz użyć kanałów, ale jeśli potrzebujesz wtyczek, sposobem na ich przejście jest interfejs . Oto trochę długa przykład prostej architektury wtyczek, która minimalizuje kod, który musi być zapisany w głównym pliku aplikacji, aby dodać wtyczki (może to być zautomatyzowane, ale nie dunikamiczne, patrz poniżej).

Mam nadzieję, że to kierunek, którego szukasz.


1. Wtyczka Interfejsy

Więc dobrze, powiedzmy, że mamy dwie wtyczki, Fooer i wykonawcą. Najpierw zdefiniować swoje interfejsy:

// All DoerPlugins can do something when you call that method 
type DoerPlugin interface { 
    DoSomething() 
} 

// All FooerPlugins can Foo() when you want them too 
type FooerPlugin interface { 
    Foo() 
} 

2. Plugin Registry

Teraz nasza podstawowa aplikacja ma rejestru wtyczki. Robię coś quicky i brudne tutaj, żeby dostać ideę całej:

package plugin_registry 

// These are are registered fooers 
var Fooers = []FooerPlugin{} 

// Thes are our registered doers 
var Doers = []DoerPlugin{} 

Teraz możemy narazić metod, aby dodać pluginy do rejestru. Prostym sposobem jest dodanie jednego do każdego typu, ale możesz przejść z bardziej złożonymi elementami refleksyjnymi i mieć jedną funkcję.Ale zwykle idź, spróbuj zachować rzeczy proste :)

package plugin_registry 

// Register a FooerPlugin 
func RegisterFooer(f FooerPlugin) { 
    Fooers = append(Fooers, f) 
} 

// Register a DoerPlugin 
func RegisterDoer(d DoerPlugin) { 
    Doers = append(Doers, d) 
} 

3. wdrażaniu i rejestracji wtyczki

Teraz załóżmy, że jest to Twoja moduł wtyczki. Tworzymy wtyczkę, która jest wykonawcą, a w naszej paczce zapisujemy ją w naszym pakiecie w postaci init(). init() dzieje się raz podczas uruchamiania programu dla każdego zaimportowanego pakietu.

package myplugin 

import (
    "github.com/myframework/plugin_registry" 
) 
type MyPlugin struct { 
    //whatever 
} 

func (m *MyPlugin)DoSomething() { 
    fmt.Println("Doing something!") 
} 

Ponownie, jest tutaj "init magia", który rejestruje pakiet automatycznie

func init() { 
    my := &MyPlugin{} 
    plugin_registry.RegisterDoer(my) 
} 

4. Importowanie wtyczek rejestruje je automatycznie

A teraz tylko rzeczą, którą musimy zmienić, jest to, co importujemy do naszego głównego pakietu. Od Go nie ma dynamicznych importów ani linków, to jedyna rzecz, którą musisz napisać. To dość trywialne, aby utworzyć skrypt go generate, który wygeneruje główny plik , zaglądając do drzewa plików lub pliku konfiguracyjnego i znajdując wszystkie wtyczki do zaimportowania. Nie jest dynamiczny, ale może być zautomatyzowany. Ponieważ główny importuje wtyczkę dla efektu ubocznego rejestracji, importuje uses the blank identifier to avoid unused import error.

package main 

import (
    "github.com/myframework/plugin_registry" 

    _ "github.com/d00dzzzzz/myplugin" //importing this will automaticall register the plugin 
) 

5. W rdzenia aplikacji

A teraz nasza podstawowa aplikacja nie wymaga zmiany kodu, aby móc współpracować z wtyczek:

func main() { 


    for _, d := range plugin_registry.Doers { 
     d.DoSomething() 
    } 

    for _, f := range plugin_registry.Fooers { 
     f.Foo() 
    } 

} 

I o to chodzi. Należy pamiętać, że rejestr wtyczki powinien być oddzielnym pakietem , który można zaimportować zarówno rdzeniem aplikacji, jak i wtyczkami, aby nie było importu okrężnego.

Oczywiście do tego miksu można dodać procedury obsługi zdarzeń, ale jak wykazałem, nie jest to konieczne.

Powiązane problemy