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,
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.
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
Pourquoi avez-vous besoin de DI en premier lieu? Les objectifs très importants de l'ID sont les suivants.
Cela peut également être appelé une séparation des intérêts.
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.
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
...........
}
}
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")
}
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)
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.
―― 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.
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")
}
--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
}
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).
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
[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