依存性と依存性の注入・逆転を理解する
はじめに
依存性とは
依存性とは「依存するオブジェクト」のことです。
と言われても全然ピンと来ないと思います。
そこで、今回は簡単なプログラムを用いて、依存性が発生しているケースと、解消方法である依存性の注入・逆転について、解説していきたいと思います。
依存性が発生しているケース
-
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 {
String getName() {
return 'Tom';
}
}
-
NameRepository
のインターフェースであるINameRepository
を定義しました。
import 'name_repository.dart';
class NameModel {
NameModel(this._nameRepository);
final INameRepository _nameRepository;
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;
void callHello() {
final String name = _nameRepository.getName();
print('Hello$name');
}
}
-
INameRepository
の定義をname_model.dart
に移動させました。
import 'name_model.dart';
class NameRepository implements INameRepository {
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