依存性と依存性の注入・逆転を理解する
はじめに
依存性とは
依存性とは「依存するオブジェクト」のことです。
と言われても全然ピンと来ないと思います。

そこで、今回は簡単なプログラムを用いて、依存性が発生しているケースと、解消方法である依存性の注入・逆転について、解説していきたいと思います。
依存性が発生しているケース
-
NameRepositoryはユーザ名を返します。
class NameRepository {
String getName() {
return 'Tom'
}
}
-
NameModelは内部でNameRepositoryを使用してユーザの名前を取得します。
import 'name_repository.dart';
class NameModel {
//内部でNameRepositoryをインスタンス化している
final nameRepository _nameRepository = NameRepository();
void callHallo() {
final String name = _nameRepository.getName();
print('Hello $name');
}
}
-
main()の処理ではNameModelのcallHello()メソッドを実行します。
import 'name_model.dart';
void main() {
final nameModel nameModel = NameModel();
nameModel.callHello();
}
この場合NameModel はNameRepositoryがないと動きないため、NameModel はNameRepositoryに依存していると言えます。
これが依存性と呼ばれるものになります。
この依存性は何が問題となるのか、次は私が製造業の出身であることから、部品メーカーとその部品を使って製品を作っている自動車のメーカーを例に解説したいと思います。
依存性の問題を解説
部品メーカーは独自に部品をつくっていて、その部品を大手のメーカーに提供しています。

自動車メーカーは渡された部品を使って、どういう製品を作るか設計して実装(製造)を行なっています。

このような場合、部品メーカーとの関係が悪くなったり、また部品メーカーが倒産した場合、自動車メーカーは製品を作ることができなくなってしまいます。

なぜならば、この場合自動車メーカーは特定の部品メーカーに依存しているためです。
依存性の問題を解決するために
ではどうすれば、自動車メーカーは依存するせずに製品を製造することができるのでしょうか。
そのためには、
- 自動車メーカーはまず作りたい車を設計する。
- その車に必要となる部品の仕様書を作成し、部品メーカーにそれを作るよう依頼する。
- 部品メーカーは決められた仕様に基づいて部品を作成し、自動車メーカーに納品する。
このような構図になれば、自動車メーカーはもはや特定の部品メーカーに依存していません。
仕様を満たす部品をより安くつくることのできる部品メーカーが見つかったのであれば、そちらに切り替えることも可能になります。
またこの場合、部品メーカーは自動車メーカーに依存することになるため、依存性の構図が逆転しています。
これを依存性の逆転と言います。
依存性の逆転をプログラムで実装
部品メーカーと自動車メーカーの例で依存性と依存性の逆転がわかったところで、次は先ほどのプログラムの依存性を逆転してみましょう。
依存性を逆転させるためには、依存性を注入するといった段階を踏む必要があります。
class NameRepository {
String getName() {
return 'Tom';
}
}
import 'name_repository.dart';
class NameModel {
NameModel(this._nameRepository);
final NameRepository _nameRepository;
void callHello() {
final String name = _nameRepository.getName();
print('Hello,$name');
}
}
- これにより
NameModelは自身が必要とするNameRepositoryを外部から受け取るようになります。
import 'name_model.dart';
import 'name_repository.dart';
void main() {
final NameRepository nameRepository = NameRepository();
final NameModel name = NameModel(nameRepository);
name.callHello();
}
-
main()の処理でNameRepositoryをインスタンス化しNameModelに渡しています。 - これにより
NameModelに例えばDummyNameRepositoryを渡すこともできるようになります。
初期のコードでは、NameModel が自分でNameRepositoryをインスタンス化していましたが、
これをNameModelを使用するmain()から渡すことで依存性の注入をすることができます。
しかし、引き続き NameModel は NameRepository に依存していているため、修正が必要です。
依存性の逆転をプログラムで実装(続き)
abstract class INameRepository {
String getName();
}
class NameRepository implements INameRepository {
@override
String getName() {
return 'Tom';
}
}
-
NameRepositoryのインターフェースであるINameRepositoryを定義しました。
import 'name_repository.dart';
class NameModel {
NameModel(this._nameRepository);
final INameRepository _nameRepository;
@override
void callHello() {
final String name = _nameRepository.getName();
print('Hello,$name');
}
}
これによりNameModel は NameRepository ではなく INameRepository というインターフェースに依存するようになりました。
import 'name_model.dart';
import 'name_repository.dart';
void main() {
final INameRepository nameRepository = NameRepository();
final NameModel name = NameModel(nameRepository);
name.callHello();
}
しかしこの場合、name_repositpry.dartのファイルを削除した場合、name_model.dartでエラーが発生します。 これはname_model.dartのコードは、name_repositpry.dartに依存しているためです。
そのため、引き続き下記のように修正します。
abstract class INameRepository {
String getName();
}
class NameModel {
NameModel(this._nameRepository);
final INameRepository _nameRepository;
@override
void callHello() {
final String name = _nameRepository.getName();
print('Hello$name');
}
}
-
INameRepositoryの定義をname_model.dartに移動させました。
import 'name_model.dart';
class NameRepository implements INameRepository {
@override
String getName() {
return 'Tom';
}
}
-
NameRepositoryはname_model.dartからインターフェースを import するようになりました。
import 'name_model.dart';
import 'name_repository.dart';
void main() {
final INameRepository nameRepository = NameRepository();
final NameModel name = NameModel(nameRepository);
name.callHello();
}
-
NameRepositoryはname_model.dartで定義されているINameRepositoryに依存するようになり、依存性が逆転しました。
これにより、name_repository.dartのファイルが削除されてもname_model.dartを修正する必要がなくなります。
逆に name_model.dart が削除されると name_repository.dartではエラーが起きるようになり、最初と依存関係が逆転していることがわかります。
最後に
現実世界に例えることで、自分はとても腑に落ちることができました。
今回の記事を見て、誰かの力になれればとても嬉しいです。
長文となりましたが、ここまで見てくださりありがとうございます。
Discussion