依存性注入の基本

依存性注入の基本


この記事では、単純な言語での依存性注入の基本(Eng。Dependency Injection、DI )、およびこのアプローチを使用する理由について説明します。 この記事は、依存性注入とは何かを知らない人、またはこの手法を使用する必要性を疑う人を対象としています。 それでは始めましょう。


中毒とは何ですか?


最初に例を見てみましょう。 以下に示すように、 ClassAClassB 、およびClassCがあります。


 class ClassA { var classB: ClassB } class ClassB { var classC: ClassC } class ClassC { } 

ClassAクラスにはClassBクラスのインスタンスが含まれていることがわかります。したがって、 ClassAクラスはClassBクラスに依存していると言えClassB 。 なんで? ClassAクラスはClassBクラスを正しく動作させる必要があるためClassBClassBクラスはClassAクラスの依存関係であるとも言えます。


続行する前に、アプリケーションですべての作業を行うために1つのクラスが必要なわけではないため、この関係が良好であることを明確にしたいと思います。 ロジックを異なるクラスに分割する必要があり、それぞれが特定の機能を担当します。 この場合、クラスは効果的に相互作用できます。


依存関係を扱う方法は?


依存性注入タスクを完了するために使用される3つのメソッドを見てみましょう。


最初の方法:依存クラスで依存関係を作成する


簡単に言えば、必要なときにいつでもオブジェクトを作成できます。 次の例を見てください。


 class ClassA { var classB: ClassB fun someMethodOrConstructor() { classB = ClassB() classB.doSomething() } } 

とても簡単です! 必要なときにクラスを作成します。


メリット



短所



各クラスはその仕事のみを行う必要があります。

したがって、クラスが自分のタスク以外のことを担当することは望ましくありません。 この場合の依存関係の実装は、それらに設定する追加のタスクです。


2番目の方法:カスタムクラスを介して依存関係を注入する


そのため、依存クラス内に依存関係を注入するのは得策ではないことを理解して、別の方法を調べてみましょう。 ここで、依存クラスはコンストラクタ内で必要なすべての依存関係を定義し、ユーザークラスがそれらを提供できるようにします。 これは私たちの問題の解決策ですか? 少し後で確認します。


以下のサンプルコードをご覧ください。


 class ClassA { var classB: ClassB constructor(classB: ClassB){ this.classB = classB } } class ClassB { var classC: ClassC constructor(classC: ClassC){ this.classC = classC } } class ClassC { constructor(){ } } class UserClass(){ fun doSomething(){ val classC = ClassC(); val classB = ClassB(classC); val classA = ClassA(classB); classA.someMethod(); } } view rawDI Example In Medium - 

これで、 ClassAはコンストラクター内のすべての依存関係を取得し、何も初期化せずにClassBクラスのメソッドを単純に呼び出すことができます。


メリット



欠点



2番目の方法は明らかに1番目の方法よりもうまく機能しますが、まだ欠点があります。 より適切なソリューションを見つけることは可能ですか? 3番目の方法を検討する前に、まず依存性注入の概念について説明しましょう。


依存性注入とは何ですか?


依存性注入は、依存クラスが何もする必要がない場合に、依存クラス外の依存関係を処理する方法です。

この定義に基づいて、最初のソリューションは明らかに依存性注入のアイデアを使用しません。2番目の方法は、依存クラスが依存関係を提供するために何もしないということです。 しかし、2番目の解決策はまだ悪いと思います。 なぜ?!


依存性注入定義は、依存性のある作業が行われる場所については何も述べていないので(依存クラスの外部を除く)、開発者は依存性注入の適切な場所を選択する必要があります。 2番目の例からわかるように、ユーザークラスは適切な場所ではありません。


より良い方法は? 依存関係を処理する3番目の方法を見てみましょう。


第三の方法:私たちの代わりに他の誰かに依存関係を処理させます


最初のアプローチでは、依存クラスが独自の依存関係を取得する責任があり、2番目のアプローチでは、依存クラスの処理を依存クラスからユーザークラスに移動しました。 依存関係を処理できる誰かがいて、その結果、依存クラスもユーザークラスも仕事をしないと想像してみましょう。 このメソッドを使用すると、アプリケーションの依存関係を直接操作できます。


依存性注入の「クリーンな」実装(個人的な意見)

依存関係を処理する責任はサードパーティにあるため、アプリケーションのどの部分も依存関係を操作しません。

依存性注入は、テクノロジー、フレームワーク、ライブラリーなどではありません。 これは単なるアイデアです。 考え方は、依存クラスの外側の依存関係(できれば特別に割り当てられた部分)を使用することです。 ライブラリやフレームワークを使用せずにこのアイデアを適用できます。 ただし、作業を簡素化し、テンプレートコードを記述する必要がないため、通常、依存関係を実装するためのフレームワークを使用します。


依存性注入フレームワークには、2つの固有の特性があります。 他の追加機能が利用できる場合がありますが、これらの2つの機能は常に存在します。


まず、これらのフレームワークは、実装する必要があるフィールド(オブジェクト)を決定する方法を提供します。 一部のフレームワークは、 @Inject注釈を使用してフィールドまたはコンストラクターに注釈を付けることでこれを行いますが、他のメソッドもあります。 たとえば、 KoinはKotlinの組み込み言語機能を使用して実装を決定します。 Injectは、依存関係がDIフレームワークによって処理される必要があることを意味します。 コードは次のようになります。


 class ClassA { var classB: ClassB @Inject constructor(classB: ClassB){ this.classB = classB } } class ClassB { var classC: ClassC @Inject constructor(classC: ClassC){ this.classC = classC } } class ClassC { @Inject constructor(){ } } 

次に、フレームワークを使用すると、各依存関係を提供する方法を決定できます。これは、個別のファイルで行われます。 おおよそ次のようになります(これは単なる例であり、フレームワークによって異なる場合があることに注意してください)。


 class OurThirdPartyGuy { fun provideClassC(){ return ClassC() //just creating an instance of the object and return it. } fun provideClassB(classC: ClassC){ return ClassB(classC) } fun provideClassA(classB: ClassB){ return ClassA(classB) } } 

したがって、ご覧のとおり、各関数は1つの依存関係を処理します。 そのため、アプリケーションのどこかでClassAを使用する必要がある場合、次のことが発生します:DIフレームワークは、 ClassCを呼び出してそれをprovideClassBに渡し、 ClassBインスタンスを受け取ることでClassCクラスのインスタンスを1つ作成します。 これはほとんど魔法です。 次に、3番目の方法の利点と利点を調べてみましょう。


メリット



 fun provideClassC(){ return AssumeClassC() } 

アプリケーション内のコードは変更されず、プロバイダーメソッドのみが変更されることに注意してください。 さらにシンプルで柔軟性のあるものはないようです。



短所



おわりに



この記事では、依存性注入の概念を扱う基本を説明し、このアイデアを使用する理由もリストしました。 独自のアプリケーションでDIを使用する方法の詳細については、さらに多くのリソースを探索できます。 たとえば、 Androidプロフェッショナルコースの高度な部分の別のセクションは、このトピック専用です。



Source: https://habr.com/ru/post/J434380/


All Articles