【SOLID】依存関係逆転の原則を完全に理解したい
SOLIDの原則とは?
SOLIDは
- 変更に強い
- 理解しやすい
などのソフトウェアを作ることを目的とした原則です。
次の5つの原則があります。
- Single Responsibility Principle (単一責任の原則)
- Open-Closed Principle (オープン・クローズドの原則)
- Liskov Substitution Principle (リスコフの置換原則)
- Interface Segregation Principle (インタフェース分離の原則)
- Dependency Inversion Principle (依存関係逆転の原則)
上記5つの原則の頭文字をとってSOLIDの原則と言います。
今回の記事では Dependency Inversion Principle (依存関係逆転の原則) について解説します。
その他の原則に関しては下記参照。
- Single Responsibility Principle (単一責任の原則)
- Open-Closed Principle (オープン・クローズドの原則)
- Liskov Substitution Principle (リスコフの置換原則)
- Interface Segregation Principle (インターフェース分離の原則)
簡単に言うと...
「使う側と使われる側の関係を見直そう」ということです。
たとえば、画面で保存ボタンを押したらDBに保存されるというシンプルな機能について考えましょう。
この場合、
- 「画面」が使う側
- 「DBに保存する処理」が使われる側
になります。
普通処理を書こうとすると、「画面」から「DBに保存する処理」を呼び出すため、
「画面」が「DBに保存する処理」に依存することになります。
たとえば、保存先がDBからCSVファイルに切り替わった場合、その影響が画面 (正確に言うと画面側での呼び出しロジック) にも影響が出てしまいます。
この影響を出ないようにするために、「使う側と使われる側の関係を見直そう」というのです。
では、使う側と使われる側の関係を見直した結果、どのようにすればよいのでしょうか?
少し詳しめに言うと...
Wikipediaでは下記のように説明されています。
上位モジュールはいかなるものも下位モジュールから持ち込んではならない。双方とも抽象(例としてインターフェース)に依存するべきである。
抽象は詳細に依存してはならない。詳細(具象的な実装内容)が抽象に依存するべきである。
先程の「画面」から「DBに保存する処理」に上記を適用すると下図のようになります。
- 上位モジュールはいかなるものも下位モジュールから持ち込んではならない。双方とも抽象(例としてインターフェース)に依存するべきである。
元々は使う側 (View) が使われる側 (Repository) に依存していましたが、
IRepositoryという抽象 (ここではインターフェース) が現れ、使う側 (View) も使われる側 (Repository) も抽象 (IRepository) に依存しています。
- 抽象は詳細に依存してはならない。詳細(具象的な実装内容)が抽象に依存するべきである。
IRepositoryにSaveというメソッドが定義されており、Repositoryはそれを継承して使っています。これは、詳細 (Repository) の具体的な実装内容が、抽象 (IRepository) に依存しているということです。
もう少し具体例を用いながら見ていきましょう。
今回使用する例
テレビの録画を例に考えてみましょう。
テレビの録画機器として、ビデオ、ブルーレイディスク、HDDなどがあります。
これをコードに落とし込んでいきましょう。
依存関係逆転の原則に違反した例
ビデオ、ブルーレイディスク、HDDの3つのクラスを作り、
テレビから呼び出すという想定で考えてみましょう。
class Television
{
public void Record()
{
if (ConnectsVideo())
{
Video video = new Video();
video.Record();
return;
}
if (ConnectsBluRay())
{
Blu_ray bluRay = new Blu_ray();
bluRay.Record();
return;
}
HDD hdd = new HDD();
hdd.Record();
}
}
public sealed class Video
{
public void Record()
{
// ビデオに録画
}
}
public sealed class Blu_ray
{
public void Record()
{
// ブルーレイに録画
}
}
public sealed class HDD
{
public void Record()
{
// HDDに録画
}
}
テレビ側で、逐一「記録媒体が何であるか」を確認して、各記録媒体に記録指示を出すことになります。
これが、使う側が使われる側に依存している状態です。
依存関係逆転の法則を適用する
テレビと記録媒体の間にインターフェースをかませることで解決できます。
コードでの実装例は下記のようになります。
class Television
{
public void Record()
{
IRecord recorder = Factories.CreateRecord();
recorder.Record();
}
}
public interface IRecord
{
enum Recorder
{
VIDEO,
BLU_RAY,
HDD
}
public void Record();
}
public sealed class Video : IRecord
{
public void Record()
{
// ビデオに録画
}
}
public sealed class Blu_ray : IRecord
{
public void Record()
{
// ブルーレイに録画
}
}
public sealed class HDD : IRecord
{
public void Record()
{
// HDDに録画
}
}
public static class Factories
{
public static IRecord CreateRecord()
{
// テレビに接続されている機器取得
var recorder = GetRecorder();
if (recorder == IRecord.Recorder.VIDEO)
{
return new Video();
}
if (recorder == IRecord.Recorder.BLU_RAY)
{
return new Blu_ray();
}
return new HDD();
}
}
Factoryを使うことで、テレビ側での呼び出しを簡素にしています。
これにより、ビデオなどの具象クラスになにかの変更があったとしても、その変更の影響が使う側には伝わりにくくなります。
また、それ以外のメリットもあります。
テスト容易姓
依存関係逆転の法則を適用することは、テストをしやすくなることにも繋がります。
例えば、上記例では、
- ビデオ
- ブルーレイディスク
- HDD
などの具体的なものに繋いでの動作確認が必要になります。
もし、それらの機器が手元にない場合、テストをすることができません。
このように、外部機器との接続が必要なものの場合、テスト用のクラス (Fakeクラスなど) を用意しておくことで
テストがしやすくなります。
例えば、Factoriesにこのように書いておくことで、デバッグ時は常にFakeから値を返すということができます。
public static class Factories
{
public static IRecord CreateRecord()
{
// ここを追加
#if DEBUG
return new Fake();
#endif
// テレビに接続されている機器取得
var recorder = GetRecorder();
if (recorder == IRecord.Recorder.VIDEO)
{
return new Video();
}
if (recorder == IRecord.Recorder.BLU_RAY)
{
return new Blu_ray();
}
return new HDD();
}
}
まとめ
依存関係逆転の法則とは「使う側と使われる側の関係を見直そう」ということでした。
具体的には、
- 使う側も使われる側も抽象に依存させる
- 詳細の処理内容を抽象に依存させる
ということです。
そうすることで下記のようなメリットが得られます。
- 変更に強い
- 理解しやすい
尚、今回使用したソースはこちらに上がっています。
参考文献
Author And Source
この問題について(【SOLID】依存関係逆転の原則を完全に理解したい), 我々は、より多くの情報をここで見つけました https://qiita.com/k2491p/items/686ee5dd72b4baf9a81a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .