Différents concepts tels que l'architecture propre et l'architecture oignon ont été proposés, J'étais frustré après l'avoir étudié. Parce que je ne savais pas grand-chose sur l'ID et la dépendance en premier lieu. Oui, j'étudiais dans le mauvais ordre. Eh bien, je suis content de l'avoir remarqué.
Par conséquent, j'ai essayé de résumer la DI avec autant de détails que possible d'une manière facile à comprendre. Après avoir lu cet article, vous serez certainement en mesure de comprendre et de mettre en œuvre les avantages de ** DI **
DI est une abréviation pour Dependency Injection
Pour expliquer brièvement le modèle de conception DI, Définissez l'objet en tant qu'interface et l'utilisateur doit utiliser l'interface au lieu de l'objet d'implémentation. Les objets d'implémentation peuvent être remplacés en les injectant dans l'interface depuis l'extérieur.
Le conteneur DI est un ** framework ** pour aider à réaliser DI
Pendant que j'apprenais, il y avait des mots que je ne comprenais pas et j'étais très confus. Alors faisons correspondre la définition des mots en premier.
・ D'où vient-il de l'extérieur? À partir d'autres objets!
・ Qu'est-ce que la dépendance? Un objet B qui nécessite un objet A En d'autres termes, l'objet A connaît le contenu de l'objet B. Pour le moment, vous pensez peut-être que vous dépendez de New
・ Vous dites que vous n'en dépendez pas, mais vous en dépendez, non? Vous utilisez un objet A avec un objet B injecté dans l'interface A! !! Cela dépend-il? Droite? J'ai toujours pensé. C'était la chose la plus ennuyeuse. Pour être précis, l'objet B dépend de l'interface A. Ainsi, l'objet B ne dépend pas de l'objet A. (Parfois cela dépend de l'abstraction) Ce qui est bien à ce sujet, c'est que les objets B peuvent être implémentés sans connaître les objets A. Les détails seront décrits plus tard.
** "Pourquoi utilisez-vous DI?" C'est le plus important. ** ** Je ne pense pas qu'il puisse être utilisé même si j'utilise DI sans le savoir. Il est important de toujours être conscient du ** but de l'utilisation de la méthode "pour quoi". Ensuite, j'écrirai que vous pouvez être heureux en utilisant DI.
** ・ Parce qu'il ne dépend pas d'une base de données externe, il résiste aux changements. ** ** Raison: car il injecte l'objet d'implémentation via l'interface Le côté logique qui utilise l'interface peut l'utiliser sans connaître l'objet d'implémentation.
** ・ Test unitaire facile ** Raison: comme ci-dessus, mais en se moquant de la DB (comme une fausse DB) Vous pouvez tester sans changer la logique du côté qui utilise l'interface. Les deux résultats sont problématiques même si vous changez parce que vous ne connaissez pas l'extérieur (indépendant).
** ・ Simplement plus facile à coder. ** ** Raison: supposons que vous ayez les classes A et B. A a besoin de B (dépendance). Dans ce cas, A ne peut pas être implémenté sans B. Cependant, si A implémente en utilisant l'interface de B, il peut être implémenté sans B. L'injection d'un objet B via une interface est identique à A en utilisant B.
Jusqu'à présent, nous avons expliqué le concept et le mécanisme de l'ID. À partir de là, nous examinerons le code réel et le comprendrons. Même si vous ne comprenez pas au début, vous devriez pouvoir le comprendre si vous le lisez plusieurs fois! En fait, je l'étais.
Le code à créer est composé des trois suivants. ・ Couche de référentiel ・ ・ ・ Couche de connexion à DB ・ Couche de service ・ ・ ・ Couche qui appelle le référentiel ・ Couche d'appel ・ ・ ・ Couche qui exploite l'ensemble
Tout d'abord, examinons le code désordonné qui a des dépendances. (* Je pense avoir écrit ce code lorsque j'ai commencé la programmation.)
couche de référentiel La base de données créée est donnée directement au référentiel d'utilisateurs. (Mauvais: la couche du référentiel dépend de db. Je connais mysql)
package repository
type UserRepository struct {
db sql.DB
}
func NewUserRepository() *UserRepository {
//Création de DB
db, _ := sql.Open("mysql", "root:@/database")
//Création de la base de données directement dans le référentiel
return &UserRepository{*db}
}
couche de service Le référentiel est nouveau et donné directement au service. (Mauvais point: la couche de service dépend du référentiel.)
package service
type UserService struct {
ur repository.UserRepository
}
func NewUserService() *UserService {
//Créer un référentiel
ur := repository.NewUserRepository()
//Référentiel créé directement au service
return &UserService{*ur}
}
Couche d'appel Si le service est nouveau, un référentiel fixe et une base de données seront créés.
package main
func main() {
us := service.NewUserService()
//Gonyo Gonyo nous utilise
}
J'ai vu du mauvais code une fois. Vous pouvez voir que cela dépend beaucoup. Reconfirmée juste au cas où, une dépendance est un état dans lequel un objet connaît le contenu d'un objet.
De là, nous allons regarder un très beau code qui introduit DI. (* Quand je vois pour la première fois le code qui utilise DI, il a l'air très sale ...)
J'ai oublié une chose importante avant de regarder le code. Il y a quatre manières de réaliser l'ID. 1.Constructor Injection 2.Setter Injection 3.Interface Injection 4.Field Injection
Cette fois, j'utiliserai 1 injection de constructeur. Je pense que c'est le meilleur pour moi.
couche de référentiel Dans NewUserRepository, db est utilisé comme constructeur. Par conséquent, cela ne dépend pas de db. De plus, return renvoie l'interface. Cela élimine également la dépendance entre la couche de référentiel et la couche de service.
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 nom de table (nom de colonne 1),Nom de colonne 2,……)")
}
couche de service NewUserService utilise une interface de référentiel dans son constructeur. Par conséquent, cela ne dépend pas du référentiel. De plus, return renvoie l'interface. Cela élimine également la dépendance entre la couche de service et la couche utilisateur.
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)
}
Couche côté utilisateur ur := repository.NewUserRepository(db) N'importe quelle base de données peut être acceptée tant qu'il s'agit d'une base de données.
us := service.NewUserService(ur) Tant que ur (référentiel) est un référentiel, tout référentiel peut être accepté.
Cela facilite son remplacement par un autre référentiel ou DB au moment du test. Comme ils ne se connaissent pas, il n'y a pas de dépendances et des modifications peuvent être apportées facilement.
(* Pour faciliter l'explication, la création de db est écrite en main)
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 nous utilise
}
J'ai expliqué DI. En le mettant en œuvre ou en le regroupant dans un article comme celui-ci, L'intérieur de ma tête était très rafraîchissant. De plus, même si je pense le comprendre, je pense que ce ne sera peut-être pas possible s'il est mis en œuvre de manière inattendue. Si vous étudiez la DI, essayez de la mettre en œuvre vous-même.
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
Recommended Posts