[GO] S'il n'y avait pas de conteneurs DI dans le monde.

Cet article est l'article du 9ème jour de Just a Group Advent Calendar 2019.

Il y a tellement d'articles sur le net que je parle rarement de conteneurs DI. (~~ Titre de pêche ~~)

Dans cet article, le but du conteneur DI,

Séparation de la création et de l'utilisation des objets

Inversion de contrôle

Je vais parler de mm

S'il n'y a pas de conteneur DI dans le monde et que vous devez faire manuellement ce que fait le conteneur DI, écrivons réellement le code pour voir quel type d'implémentation est possible.

Connaissances préalables

Qu'est-ce qu'un conteneur DI?

Un framework qui fournit aux applications la fonctionnalité DI (Dependency Injection).

Pour plus de détails, veuillez consulter les articles suivants.

Qu'est-ce qu'un conteneur DI Conteneur DI / DI, comprenez-vous bien ...? -Qiita

Que vas-tu faire

Pourquoi avez-vous besoin de DI en premier lieu? Les objectifs très importants de l'ID sont les suivants.

1. Séparez la création et l'utilisation d'objets

Cela peut également être appelé une séparation des intérêts.

2. Inversion de contrôle

Dans SOLID, c'est le principe de l'inversion des dépendances.

L'ID est un moyen d'atteindre ces objectifs.

Outre la DI, il existe différentes manières d'y parvenir.

Dans cet article, je vais présenter certaines des différentes façons d'atteindre les objectifs ci-dessus dans la base de code.

Quel est le problème sans "séparer la création et l'utilisation des objets"?

1. Il est très difficile à tester car il n'y a pas de séparation des responsabilités uniques.

2. La logique de création d'objet est dispersée partout, créant parfois une duplication significative.

Lorsque je teste la méthode de recherche, une instance JobRepositoryImpleWithMysql est effectivement créée, donc Si JobRepositoryImpleWithMysql est lié à DB, il est très difficile à tester.


class SearchUseCase {
  def search = {
    val repositoryImpleWithMysql = new JobRepositoryImpleWithMysql
    ...........
  }
}

Que puis-je faire sans «inverser le contrôle»?

1. En s'appuyant sur la classe concrète, si la classe concrète est modifiée, elle devra être modifiée.

2. Il est difficile de tester quand la classe concrète a un lien avec le DB.

Si vous remplacez JobRepositoryImpleWithMysql par jobRepositoryImpleWithElasticSearch, Le côté utilisateur (SearchUseCase) doit également apporter des corrections.



class SearchUseCase {
  def search = {
    val repositoryImpleWithMysql = new JobRepositoryImpleWithMysql
    ...........
  }
}


class JobRepositoryImpleWithMysql {
  def getJobs: Seq[String] = Seq("job1", "job2")
}

Un modèle qui réalise "la séparation de la création et de l'utilisation des objets"

Il existe trois modèles principaux. En cela, je présenterai ceux en gras avec le code réel.

Puisque ** cake pattern ** et ** abstract factory pattern ** réalisent également une "inversion de contrôle", nous ne regarderons d'abord que le ** modèle de constructeur **.

Le motif du gâteau semble être un motif particulier à la scala. (Référence)

Modèle de constructeur

package study.ConstractPattern

class Main {

  def main(args: Array[String]): Unit = {
    val useCase = new SearchUseCase(new JobRepositoryImpleWithMysql)
    useCase.search
  }
}

class SearchUseCase(jobRepositoryImpleWithMysql: JobRepositoryImpleWithMysql) {
  //JobRepositoryImple est délégué à SearchUseCase dans l'argument du constructeur.
  //Au lieu d'injecter des objets avec le nouveau JobRepositoryImple sans searchUseCase
  //En injectant à partir de l'endroit où l'objet SearchUseCase est déclaré
  //L'injection de dépendance peut être réalisée de l'extérieur.
  //Vous pouvez l'utiliser de l'extérieur! Injecter un objet.
  
  val jobRepositoryImpleInstance = jobRepositoryImpleWithMysql

  def search: Seq[String] = {
    jobRepositoryImpleInstance.getJobs
  }
}

trait JobRepository {
  def getJobs: Seq[String]
}

class JobRepositoryImpleWithMysql extends JobRepository {
  override def getJobs: Seq[String] = Seq("job1", "job2")
}

class jobRepositoryImpleWithElasticSearch extends JobRepository {
  override def getJobs: Seq[String] = Seq("job1", "job2")
}


Comme je l'ai écrit dans le commentaire, je peux séparer la création et l'utilisation de l'objet en injectant l'objet à utiliser comme argument constructeur.

Ce que tu peux faire

Choses impossibles

―― En fin de compte, vous devez créer un nouvel objet au moment de l'exécution de l'application, vos responsabilités y sont donc concentrées.

ex) Lorsque jobRepositoryImpleWithMysql est changé en jobRepositoryImpleWithElasticSearch, vous devez apporter des modifications.

Motif qui réalise l'inversion de contrôle

Il existe les méthodes suivantes.

abstract factory pattern

package study.abstractFactoryPattern

class Main {
  def main(args: Array[String]): Unit = {
    val searchUseCase = new SearchUseCase
    searchUseCase.search
  }
}


//L'utilisateur n'a pas besoin de connaître la classe concrète.
//Contrôler la création d'objets côté application

class SearchUseCase {
  def search = {
    val repository: JobRepository = JobRepositoryFactory.createJobRepository
    repository.getJobs
  }
}

// abstract factory
trait AbstractFactory {
  def createJobRepository: JobRepository
}

// abstract product
trait JobRepository {
  def getJobs: Seq[String]
}

// concrete factory
object JobRepositoryFactory extends AbstractFactory {
  override def createJobRepository: JobRepository = new JobRepositoryImple
}

// concrete product
class JobRepositoryImple extends JobRepository {
  override def getJobs: Seq[String] = Seq("job1", "job2")
}


Ce que tu peux faire

--Dans factory.createJobRepository, la partie création d'objet est exécutée. En séparant le traitement, la séparation pour chaque intérêt peut être réalisée.

cake pattern

package study.cakePattern

class Main {
  def main(args: Array[String]): Unit = {
    //Injection de searchUseCase lui-même
    val useCase = ComponentRegistry.searchUseCase
    useCase.search
  }
}


//Composant abstrait
//Entourez-le de composants et créez un espace de noms pour chacun
trait JobRepositoryComponent {
  val jobRepository: JobRepository

  trait JobRepository {
    def search: Unit
  }
}

//Composants en béton
//Entourez-le de composants et créez un espace de noms pour chacun
trait JobRepositoryComponentImple extends JobRepositoryComponent {
  class JobRepositoryImple extends JobRepository {
    override def search: Unit = println("create user")
  }
}

//Composants clients
//Déclarer les dépendances sur UserRepository à l'aide d'annotations de type autonome
trait SearchUseCaseComponent { self: JobRepositoryComponent =>
  class SearchUseCase {
    def search = {
      //Je veux que ce jobRepository soit injecté
      self.jobRepository.search
    }
  }
}

//Injecteur (rôle du conteneur DI)
//SearchUseCaseComponent déclare JobRepositoryComponent avec sa propre annotation de type
//JobRepositoryComponentImple doit également être mélangé dans
object ComponentRegistry extends SearchUseCaseComponent with JobRepositoryComponentImple {
  override val jobRepository = new JobRepositoryImple
  val searchUseCase = new SearchUseCase
}

Ce que tu peux faire

Dans la méthode de recherche de la classe SearchUseCase, l'inversion de contrôle est réalisée en s'appuyant sur la classe abstraite (JobRepository) au lieu de dépendre de la classe concrète (JobRepositoryImple).

Résumé

Nous avons vu plusieurs manières comme décrit ci-dessus. En utilisant le conteneur DI, j'ai réalisé la commodité du conteneur DI car il effectue un traitement compliqué en interne.

Y a-t-il des inconvénients à introduire un conteneur DI?

Merci jusqu'à la fin mm

Article de référence

[Dependency injection-Wikipedia](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3% A8% E5% 85% A5)

Réflexion: Design Pattern (Abstract Factory Pattern) --Qiita

Scala dans la bataille réelle: injection de dépendances à l'aide du modèle Cake (DI) | eed3si9n

[DI (Cake Pattern Introduction) avec Scala | TECHSCORE BLOG](https://www.techscore.com/blog/2012/03/27/scala%E3%81%A7di-%EF%BC%88cake-pattern% E5% B0% 8E% E5% 85% A5% E7% B7% A8% EF% BC% 89 /)

Recommended Posts

S'il n'y avait pas de conteneurs DI dans le monde.
Déterminer s'il y a des oiseaux dans l'image
Programmation pour combattre dans le monde ~ 5-1
Programmation pour combattre dans le monde ~ 5-5,5-6
Programmer pour combattre dans le monde 5-3
Malheureusement, il n'y a pas de sens d'unité dans la méthode where
Il n'y a pas de commutateur en python
Le brevet le plus cité au monde
Programmation pour combattre dans le monde ~ 5-2
Si vous obtenez une erreur sans attribut dans boto3, vérifiez la version
Vérifiez si l'URL existe en Python
Y a-t-il NaN dans le DataFrame pandas?
Si branche en fonction de l'existence ou non d'un élément spécifique dans la liste
Vérifiez si les caractères sont similaires en Python
Dans bash, "supprimez le fichier s'il existe".
N'y a-t-il pas une valeur par défaut dans le dictionnaire?
Je veux gagner s'il y a le grand prix de visualisation le plus inutile au monde ・ Apprenez la visualisation en faisant évoluer la fonction OP