[GO] Wenn es keine DI-Container auf der Welt gäbe.

Dieser Artikel ist der Artikel zum 9. Tag von Just a Group Adventskalender 2019.

Es gibt so viele Artikel im Internet, dass ich selten über DI-Container spreche. (~~ Angeltitel ~~)

In diesem Artikel wird der Zweck des DI-Containers,

Trennung von Objekterstellung und -nutzung

Umkehrung der Kontrolle

Ich werde über mm sprechen

Wenn es keinen DI-Container auf der Welt gibt und Sie manuell tun müssen, was der DI-Container tut, schreiben wir den Code tatsächlich, um zu sehen, welche Art von Implementierung möglich ist.

Vorausgesetztes Wissen

Was ist ein DI-Container?

Ein Framework, das Anwendungen mit DI-Funktionen (Dependency Injection) versorgt.

Einzelheiten finden Sie in den folgenden Artikeln.

Was ist ein DI-Container DI / DI-Container, verstehst du richtig ...? -Qiita

Was wirst du tun

Warum brauchen Sie DI überhaupt? Die sehr wichtigen Zwecke von DI sind wie folgt.

1. Trennen Sie die Erstellung und Verwendung von Objekten

Dies kann auch als Interessentrennung bezeichnet werden.

2. Umkehrung der Kontrolle

In SOLID ist es das Prinzip der Abhängigkeitsumkehr.

DI ist ein Weg, um diese Ziele zu erreichen.

Neben DI gibt es verschiedene Möglichkeiten, dies zu erreichen.

In diesem Artikel werde ich einige der verschiedenen Möglichkeiten vorstellen, um die oben genannten Ziele in der Codebasis zu erreichen.

Was ist falsch, ohne "die Erstellung und Verwendung von Objekten zu trennen"?

1. Es ist sehr schwierig zu testen, da es keine Trennung der Einzelverantwortung gibt.

2. Die Objekterstellungslogik ist über den gesamten Ort verteilt und führt manchmal zu erheblichen Duplikaten.

Wenn ich die Suchmethode teste, wird tatsächlich eine JobRepositoryImpleWithMysql-Instanz erstellt Wenn JobRepositoryImpleWithMysql mit DB verknüpft ist, ist das Testen sehr schwierig.


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

Was kann ich tun, ohne die Kontrolle umzukehren?

1. Wenn Sie sich auf die konkrete Klasse verlassen, muss die konkrete Klasse geändert werden, wenn sie geändert wird.

2. Es ist schwierig zu testen, ob die konkrete Klasse eine Verbindung zur DB hat.

Wenn Sie JobRepositoryImpleWithMysql in jobRepositoryImpleWithElasticSearch ändern, Die Benutzerseite (SearchUseCase) muss ebenfalls Korrekturen vornehmen.



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


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

Ein Muster, das "Trennung von Objekterstellung und -nutzung" verwirklicht.

Es gibt drei Hauptmuster. In diesem Artikel werde ich diejenigen in Fettdruck mit dem eigentlichen Code vorstellen.

Da ** Kuchenmuster ** und ** abstraktes Fabrikmuster ** auch "Umkehrung der Kontrolle" realisieren, werden wir uns zunächst nur ** Konstruktormuster ** ansehen.

Das Kuchenmuster scheint ein Muster zu sein, das Scala eigen ist. (Referenz)

Konstruktormuster

package study.ConstractPattern

class Main {

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

class SearchUseCase(jobRepositoryImpleWithMysql: JobRepositoryImpleWithMysql) {
  //JobRepositoryImple wird im Konstruktorargument an SearchUseCase delegiert.
  //Anstatt Objekte mit neuem JobRepositoryImple ohne searchUseCase zu injizieren
  //Durch Injizieren, von wo aus das SearchUseCase-Objekt deklariert wird
  //Die Abhängigkeitsinjektion kann von außen durchgeführt werden.
  //Sie können dies von außen nutzen! Einspritzen eines Objekts.
  
  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")
}


Wie ich im Kommentar geschrieben habe, kann ich die Erstellung und Verwendung des Objekts trennen, indem ich das Objekt einfüge, das als Konstruktorargument verwendet werden soll.

Was du tun kannst

Dinge unmöglich

―― Am Ende müssen Sie zum Zeitpunkt der Anwendungsausführung ein neues Objekt erstellen, damit sich Ihre Verantwortlichkeiten dort konzentrieren.

Beispiel: Wenn jobRepositoryImpleWithMysql in jobRepositoryImpleWithElasticSearch geändert wird, müssen Sie einige Änderungen vornehmen.

Muster, das die Steuerungsinversion realisiert

Es gibt die folgenden Methoden.

abstract factory pattern

package study.abstractFactoryPattern

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


//Der Benutzer muss die konkrete Klasse nicht kennen.
//Steuern Sie die Erstellung von Objekten auf der Anwendungsseite

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")
}


Was du tun kannst

cake pattern

package study.cakePattern

class Main {
  def main(args: Array[String]): Unit = {
    //Injizieren von searchUseCase selbst
    val useCase = ComponentRegistry.searchUseCase
    useCase.search
  }
}


//Abstrakte Komponente
//Schließen Sie es in Komponenten ein und erstellen Sie für jeden einen Namespace
trait JobRepositoryComponent {
  val jobRepository: JobRepository

  trait JobRepository {
    def search: Unit
  }
}

//Betonbauteile
//Schließen Sie es in Komponenten ein und erstellen Sie für jeden einen Namespace
trait JobRepositoryComponentImple extends JobRepositoryComponent {
  class JobRepositoryImple extends JobRepository {
    override def search: Unit = println("create user")
  }
}

//Client-Komponenten
//Deklarieren Sie Abhängigkeiten von UserRepository mithilfe von Annotationen vom Typ Self
trait SearchUseCaseComponent { self: JobRepositoryComponent =>
  class SearchUseCase {
    def search = {
      //Ich möchte, dass dieses JobRepository injiziert wird
      self.jobRepository.search
    }
  }
}

//Injektor (Rolle des DI-Containers)
//SearchUseCaseComponent deklariert JobRepositoryComponent mit einer eigenen Typanmerkung
//JobRepositoryComponentImple muss ebenfalls eingemischt werden
object ComponentRegistry extends SearchUseCaseComponent with JobRepositoryComponentImple {
  override val jobRepository = new JobRepositoryImple
  val searchUseCase = new SearchUseCase
}

Was du tun kannst

Bei der Suchmethode der SearchUseCase-Klasse wird die Steuerungsumkehr realisiert, indem auf die abstrakte Klasse (JobRepository) zurückgegriffen wird, anstatt von der konkreten Klasse (JobRepositoryImple) abhängig zu sein.

Zusammenfassung

Wir haben verschiedene Wege gesehen, wie oben beschrieben. Durch die Verwendung des DI-Containers wurde mir die Bequemlichkeit des DI-Containers bewusst, da er intern eine komplizierte Verarbeitung durchführt.

Gibt es Nachteile bei der Einführung eines DI-Containers?

Danke bis zum Ende mm

Referenzartikel

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

Denken: Entwurfsmuster (abstraktes Fabrikmuster) - Qiita

Scala im aktuellen Kampf: Abhängigkeitsinjektion mit Kuchenmuster (DI) | eed3si9n

[DI (Cake Pattern Introduction) mit 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

Wenn es keine DI-Container auf der Welt gäbe.
Feststellen, ob das Bild Vögel enthält
Programmieren, um in der Welt zu kämpfen ~ 5-1
Programmierung, um in der Welt zu kämpfen ~ 5-5,5-6
Programmieren, um in der Welt zu kämpfen 5-3
Leider gibt es kein Gefühl der Einheit in der where-Methode
In Python gibt es keinen Schalter
Das am häufigsten zitierte Patent der Welt
Programmieren, um in der Welt zu kämpfen ~ 5-2
Wenn in boto3 der Fehler no attribute angezeigt wird, überprüfen Sie die Version
Überprüfen Sie, ob die URL in Python vorhanden ist
Gibt es NaN im Pandas DataFrame?
Wenn verzweigen, hängt davon ab, ob die Liste ein bestimmtes Element enthält
Überprüfen Sie, ob die Zeichen in Python ähnlich sind
In Bash "Löschen Sie die Datei, falls vorhanden".
Gibt es keinen Standardwert im Wörterbuch?
Ich möchte gewinnen, wenn es den nutzlosesten Grand Prix für Visualisierung der Welt gibt. Lernen Sie Visualisierung, indem Sie die OP-Funktion weiterentwickeln