DIを説明する時に結構うまく説明できた例え
はじめに
会社でDIについて説明した時に結構納得してもらえた例を書き残しておこうと思います。
あくまで例え話なので、皆さんの他の例えとかもあったらコメントとかしていただけると嬉しいです!
また、まだまだ駆け出しの身分なので、記載内容にミスがあるかもしれないです。
そのような場合は、あたたかくコメントにて指摘いただけますと幸いです。
DI(依存関係逆転の原則)について
書籍Clean Architecture 達人に学ぶソフトウェアの構造と設計では
ソースコードの依存関係が(具象ではなく)抽象だけを参照しているもの。それが最も柔軟なシステムである。これが『依存関係逆転の原則(DIP)』の伝えようとしていることである。
と書かれています。
また、書籍アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技では、
- 上位モジュールは下位モジュールに依存してはならない。どちらもモジュールも『抽象』に依存するべきである。
- 『抽象』は実装の詳細に依存してはならない。実装の詳細が『抽象』に依存するべきである。
と紹介されています。僕的な解釈は。
上位モジュールも下位モジュールも抽象に依存するようにしようぜ
という認識をしています。違ったらすみません。コメントでご意見をいただければと思います。
それでは、ここからはよく使う事例を紹介していきます。
世の中抽象化で成り立っている
普段僕たちが使っているものの多くは抽象化されていると思っています。
本当に身の回りの多くが抽象化されているな〜と勝手ながら思っています。
そもそも抽象化ってなんだろう
そもそも抽象化ってなんでしょう。ここからいつも話すようにしています。僕は大学院の研究をしている際に、『あ、これ抽象化だ!』って思ったことがあります。
ここからは僕の体験談です。
因子分析の要因を考えるときに、抽象化だ!と閃いた
因子分析とは多変量データに潜む共通因子を探り出すための手法として、消費者を理解するためによく使われる多変量解析手法です。
詳しい説明は省きます。例えば、高校生A君が高校のテストで
- 国語 : 50点
- 数学 : 80点
- 社会 : 40点
- 理科 : 78点
- 英語 : 70点
という点をとった時、
- 数学
- 理科
の点数が高いから、この子は理系かもしれないって思うことが多いのではないでしょうか?このように理科と数学が点数が高いから『理系』と断定する過程のことを抽象化っていうと思っています。
数学とか物理とかという具体例をまとめて抽象的にすること、これがすなわち抽象化ではないかなと思っています。
じゃあプログラマーにとっての抽象って何?
これまでは余談で、ここからはちゃんと開発中のことを書いていこうと思います。
DIのことを調べると
多分こんな感じの図をよく見るのではないでしょうか(クラスメイトかは適当です、ご勘弁)。
上位モジュールであるServiceClass
も下位モジュールであるORMRepository
もRepositoryInterface
に依存しています。
要するに、ここでは抽象は『Interfaceだ〜!』ってことになると思います。
上位モジュールも下位モジュールも抽象に依存するようにしようぜ
これを
上位モジュールも下位モジュールもどちらもInterfaceに依存するべきである
というふうに言い換えられると思います。
なんで抽象(Interface)に依存するべきなのか
これは個人的には
- より変化しづらいものへの依存が、柔軟に変更することができるから
- コードを読ませない為の試作
とかなのかなって思っています。
ただ、僕の説明もあるかもですが、現場にいきなり上の2つを言っても伝わりませんでした、
そこで、世の中の実例を使って説明をすることにしてみました。
こうするとどう変更がしやすくなるのか、実例を紹介していこうと思いおます。
電気を例に考えてみる
皆さん、電気を使いたい時って多分
- 電力会社とかに申し込みをして、電気を通す
- 電化製品をコンセントに指す
- 電気を使う
- 使用量を払う
という感じになるのかなと思います。ここですごいのは
『お金を払っていれば、コンセントに刺さっている電化製品に電気が来ること。また、その過程を一切知らなくても電気が使えるということ』
だと僕は思っています。
Interfaceは和訳すると、『境界面、接点』となります。
つまり、コンセントというInterfaceに電気を繋いでいて、お金を払えば勝手に電気が来る。しかし電化製品を使う人間は、どこの変電所からどの電気が来るのかを気にしなくて良い。ということになります。
電圧には三種類ある
電圧には
- 高圧
- 低圧
- 特別高圧
の3種類があります。それぞれ下記表のような違いがあります。
電気の種類 | 意味 |
---|---|
高圧 | 直流で750Vを超え7,000V以下、交流で600Vを超え7,000V以下であり、ビルやマンションなどの中小規模の施設で利用されることが多い |
低圧 | 電圧が直流で750V以下、交流で600V以下であり、商店や飲食店、事務所、一般家庭などで利用されることが多い |
特別高圧 | 電圧が直流・交流で7,000Vを超えるものであり、大規模な商業施設やオフィスビルなどで利用されることが多い |
という違いがあります。しかし我々電気を使うユーザーは、お金を払ってコンセントにさせば、電気が来るということだけを知って、家電を使っています。高圧とか低圧とか特圧とか気にしなくていいんです。
これが抽象化であると考えています。
Interface(コンセント)を通してRequest(お金)を渡せばResponse(電気)が返って来るということだけを知っていればいいのです。
たとえ、電気代が高くなって電気会社をAからBに変えても、コンセントにささっててお金を渡せば電気が返ってくるんです。
コードに落とし込んでみる
ここからは、コードベースで話していきます。ここではPHPを使って実装していきますが、OOPであれば多分読めるのではないかと思います。
あと、結構適当に実装します。
<?php
declare(strict_types=1);
Interface コンセントインターフェース
{
public function 電気を提供する(お金 $request):電気
}
こんな感じになるのでしょうかね。
コンセントインターフェースは、お金が払われていれば電気をくれます。
サービスクラスはこれを参照しておけば、どのから電気が来るのかとかを気にする必要が無くなります。
また、電気会社の変更もサービスクラスは気にする必要がありません。
コンセントっていう抽象化されたもののおかげで、電気って使いやすいですよね。いいですね。
開発におけるDI
では、これがどんな時に生きてくるのでしょうか。
個人的には、
- ドメイン知識を最も上位にしたい時
- 外部システムとかを挟む時
とかに使うことが多いかなと思っています。
ドメイン知識を最も上位にしたい時
これはClean architectureとか、ヘキサゴナルとかでで良くみる『Entity』とかを最上位にしたい場合に使うのかなと思っています。
よくこんな感じのものを見るのではないでしょうか。
このアーキテクチャだと、ドメイン層がインフラに依存する形になってしまいます。ドメイン層はそのアプリケーションの最も重要なオブジェクトであり、他のレイヤーに依存しないのがベストです。そこで、Interfaceを使うことで、ドメイン層がインフラ層に依存するのを防ぐことができます。
多分こんな感じになるのかと思います。ドメイン層にInterfaceをおいて、そいつをInfrastructure層が参照するみたいな感じです。
この辺は、GMOインターネット | 成瀬 允宣, PHP WEBアプリケーション設計入門――10年先を見据えて作る, PHP Conference Japan 2020で紹介されているので、是非そちらをご覧いただければと思います。とても面白い内容となっています。
外部システムとかを挟む時
例えば、エラーがあった際に管理者に通知するケースを考えてみます。
通知方法は
- メール
- Slackとかそこらへんのコミュニケーションツール
とか色々とあると思います。
アプリケーション層でトランザクションスクリプトを書いていたとして、ロールバックが走った際にメールを送るとすると、
多分こんな感じの関係になると思います。この場合
- アプリケーション層はメールサービスを気にしないといけない
- メールサービスが完成してないとアプリケーションがテストできない
とか色々とあると思います。また、アプリケーションがメールサービスに依存する形になります。
これ依存の向き的にはNGになると思います。そこで、インターフェースを挟むことで、
こうすることで、アプリケーションがメールサービスへの直接的な依存を避けられて、依存の向きをコントロールすることができます。
また、
上位モジュールも下位モジュールも抽象に依存するため、
- 外部通知サービスの取り替えが楽(メールからSlackに変更するなどが容易にできる)
- アプリケーション層は、実装の詳細を気にしなくて良い
ということになるのかなと思います。
電気の例をここで持ってくると
外部通知サービスの取り替えが楽(メールからSlackに変更するなどが容易にできる)
ここが、どこの電力会社かを気にせず、電気を使えることであり
アプリケーション層は、実装の詳細を気にしなくて良い
電気を使うユーザーは、電気の種類とかどこからくるのかを気にしなくて良くなるぜっ!ってことなのかなと思います。
まとめ
DIについて電気を例にまとめてみました。会社ではうまく説明できたと思っているのですが、いざ文章にするとまとめづらいというか、まだまだ勉強不足だなと思ったので、もっと勉強しないとな〜と思いました。
実際にソフトウェアの設計原則やプログラミング哲学みたいなものって、結構説明が難しいかなと個人的には思っています。
そこで、現実の例を踏まえて説明した後に、ソースコードレベルに落とし込むというやり方で最近は業務を取り組むようにしています。
他にもいい例とか、解釈間違っている部分がありましたら、ご教授いただけますと幸いです!!
Discussion
本筋と関係ないんですが、DIは一般的には依存注入(dependency injection)を指す単語で、依存関係逆転の原則(DIP)をDIと呼ぶことはまずないと思います。
ご指摘ありがとうございます。
おっしゃる通りです。失念していました。
タイミング見つけて記事の修正できればと思います。
わざわざコメントをつけていただき、ありがとうございます!