Verschiedene Konzepte wie saubere Architektur und Zwiebelarchitektur wurden vorgeschlagen, Ich war frustriert, nachdem ich es studiert hatte. Weil ich überhaupt nicht viel über DI und Abhängigkeit wusste. Ja, ich habe in der falschen Reihenfolge gelernt. Ich bin froh, dass ich es bemerkt habe.
Deshalb habe ich versucht, DI so leicht wie möglich auf leicht verständliche Weise zusammenzufassen. Nachdem Sie diesen Artikel gelesen haben, werden Sie definitiv die Vorteile von ** DI ** verstehen und implementieren können
DI ist eine Abkürzung für Dependency Injection
Um das DI-Entwurfsmuster kurz zu erklären, Definieren Sie das Objekt als Schnittstelle, und der Benutzer sollte die Schnittstelle anstelle des Implementierungsobjekts verwenden. Implementierungsobjekte können ersetzt werden, indem sie von außen in die Schnittstelle eingefügt werden.
DI-Container ist ein ** Framework ** zur Realisierung von DI
Während ich lernte, gab es einige Wörter, die ich nicht verstand und ich war sehr verwirrt. Lassen Sie uns also zuerst die Definition von Wörtern abgleichen.
・ Wo ist es von außen? Von anderen Objekten!
・ Was ist Abhängigkeit? Ein B-Objekt, für das ein A-Objekt erforderlich ist Mit anderen Worten, das A-Objekt kennt den Inhalt des B-Objekts. Im Moment denken Sie vielleicht, dass Sie von Neu abhängig sind
・ Du sagst, du bist nicht davon abhängig, aber du bist davon abhängig, oder? Sie verwenden ein A-Objekt mit einem B-Objekt, das in die A-Schnittstelle eingefügt wurde! !! Ist das abhängig? Richtig? Ich habe immer gedacht. Das war das nervigste. Um genau zu sein, hängt das B-Objekt von der A-Schnittstelle ab. Das B-Objekt hängt also nicht vom A-Objekt ab. (Manchmal kommt es auf die Abstraktion an) Das Gute daran ist, dass B-Objekte implementiert werden können, ohne A-Objekte zu kennen. Details werden später beschrieben.
** "Warum benutzt du DI?" Dies ist das Wichtigste. ** **. Ich glaube nicht, dass es verwendet werden kann, selbst wenn ich DI verwende, ohne dies zu wissen. Es ist wichtig, sich immer des ** Zwecks der Verwendung der Methode "wofür" bewusst zu sein. Dann werde ich schreiben, dass Sie mit DI glücklich sein können.
** ・ Da es nicht von einer externen Datenbank abhängt, ist es resistent gegen Änderungen. ** **. Grund: Weil das Implementierungsobjekt über die Schnittstelle injiziert wird Die Logikseite, die die Schnittstelle verwendet, kann sie verwenden, ohne das Implementierungsobjekt zu kennen.
** ・ Einfacher Komponententest ** Grund: Wie oben, jedoch durch Verspotten der Datenbank (wie eine gefälschte Datenbank) Sie können testen, ohne die Logik auf der Seite zu ändern, die die Schnittstelle verwendet. Beide Ergebnisse sind problematisch, auch wenn Sie wechseln, weil Sie das Äußere nicht kennen (unabhängig).
** ・ Einfach einfacher zu codieren. ** **. Grund: Angenommen, Sie haben die Klassen A und B. A braucht B (Abhängigkeit). In diesem Fall kann A nicht ohne B implementiert werden. Wenn A jedoch die Schnittstelle von B implementiert, kann es ohne B implementiert werden. Das Injizieren eines B-Objekts über eine Schnittstelle entspricht A mit B.
Bisher haben wir das Konzept und den Mechanismus von DI erklärt. Von hier aus werden wir uns den tatsächlichen Code ansehen und ihn verstehen. Auch wenn Sie es zuerst nicht verstehen, sollten Sie es verstehen können, wenn Sie es oft lesen! Eigentlich war ich.
Der zu erstellende Code besteht aus den folgenden drei. ・ Repository-Schicht ・ ・ ・ Schicht für die Verbindung zur Datenbank ・ Serviceschicht ・ ・ ・ Schicht, die das Repository aufruft ・ Anrufschicht ・ ・ ・ Schicht, die das Ganze bedient
Schauen wir uns zunächst den unordentlichen Code an, der Abhängigkeiten aufweist. (* Ich glaube, ich habe diesen Code geschrieben, als ich mit dem Programmieren angefangen habe.)
Repository-Schicht Die erstellte Datenbank wird direkt an das Benutzer-Repository übergeben. (Schlecht: Die Repository-Schicht hängt von der Datenbank ab. Ich kenne MySQL.)
package repository
type UserRepository struct {
db sql.DB
}
func NewUserRepository() *UserRepository {
//DB-Erstellung
db, _ := sql.Open("mysql", "root:@/database")
//DB direkt im Repository erstellt
return &UserRepository{*db}
}
Serviceschicht Das Repository ist neu und wird direkt an den Dienst übergeben. (Schlechter Punkt: Die Serviceschicht hängt vom Repository ab.)
package service
type UserService struct {
ur repository.UserRepository
}
func NewUserService() *UserService {
//Repository erstellen
ur := repository.NewUserRepository()
//Repository direkt für den Service erstellt
return &UserService{*ur}
}
Anrufschicht Wenn der Dienst neu ist, werden ein festes Repository und eine Datenbank erstellt.
package main
func main() {
us := service.NewUserService()
//Gonyo Gonyo benutzt uns
}
Ich habe einmal schlechten Code gesehen. Sie können sehen, dass es sehr davon abhängt. Für alle Fälle erneut bestätigt, ist eine Abhängigkeit ein Zustand, in dem ein Objekt den Inhalt eines Objekts kennt.
Von hier aus sehen wir uns einen sehr schönen Code an, der DI einführt. (* Wenn ich den Code zum ersten Mal sehe, der DI verwendet, sieht er sehr schmutzig aus ...)
Ich habe eine wichtige Sache vergessen, bevor ich mir den Code angesehen habe. Es gibt vier Möglichkeiten, DI zu erreichen. 1.Constructor Injection 2.Setter Injection 3.Interface Injection 4.Field Injection
Dieses Mal werde ich 1 Konstruktorinjektion verwenden. Ich denke, das ist das Beste für mich.
Repository-Schicht In NewUserRepository wird db als Konstruktor verwendet. Daher hängt es nicht von db ab. Außerdem gibt return die Schnittstelle zurück. Dadurch wird auch die Abhängigkeit zwischen der Repository-Schicht und der Service-Schicht beseitigt.
package repository
type UserRepository interface {
CreateUser(ctx context.Context, user *model.User)
}
type userRepository struct {
db sql.DB
}
func NewUserRepository(db *sql.DB) UserRepository {
return &userRepository{*db}
}
func (ur *userRepository) CreateUser(ctx context.Context, user *model.User) {
ur.db.Query("INSERT INTO Tabellenname (Spaltenname 1),Spaltenname 2,……)")
}
Serviceschicht NewUserService verwendet in seinem Konstruktor eine Repository-Schnittstelle. Daher hängt es nicht vom Repository ab. Außerdem gibt return die Schnittstelle zurück. Dadurch wird auch die Abhängigkeit zwischen der Serviceschicht und der Benutzerebene beseitigt.
package service
type UserService interface {
CreateUser(ctx context.Context, user *model.User)
}
type userService struct {
ur repository.UserRepository
}
func NewUserService(ur repository.UserRepository) UserService {
return &userService{ur}
}
func (us *userService) CreateUser(ctx context.Context, user *model.User) {
us.ur.CreateUser(ctx, user)
}
Ebene auf der Benutzerseite ur := repository.NewUserRepository(db) Jede Datenbank kann akzeptiert werden, solange es sich um eine Datenbank handelt.
us := service.NewUserService(ur) Solange ur (Repository) ein Repository ist, kann jedes Repository akzeptiert werden.
Dies macht es einfach, es zum Zeitpunkt des Tests durch ein anderes Repository oder eine andere Datenbank zu ersetzen. Da sie sich nicht kennen, gibt es keine Abhängigkeiten und Änderungen können leicht vorgenommen werden.
(* Zur Vereinfachung der Erklärung wird die Erstellung von db in main geschrieben.)
package main
func main() {
db, err := sql.Open("mysql", "root:@/database")
if err != nil {
panic(err.Error())
}
defer db.Close()
ur := repository.NewUserRepository(db)
us := service.NewUserService(ur)
//Gonyo Gonyo benutzt uns
}
Ich habe über DI erklärt. Indem Sie es tatsächlich implementieren oder in einem Artikel wie diesem zusammenstellen, Das Innere meines Kopfes war sehr erfrischend. Auch wenn ich denke, dass ich es verstehe, habe ich das Gefühl, dass es möglicherweise nicht möglich ist, wenn es unerwartet implementiert wird. Wenn Sie DI studieren, versuchen Sie bitte, es selbst zu implementieren.
https://recruit-tech.co.jp/blog/2017/12/11/go_dependency_injection/ https://github.com/akito0107/dicon http://inukirom.hatenablog.com/entry/di-in-go