依存性反転の原則について勉強してみる

はいどーもこんにちは、依存するより依存されたいニートです。
でも僕は社会に依存して生きていこうと思います。
ちゅーわけで本日のお題は「依存性反転の原則」ですよ。

依存性反転の原則ってなんやねん

依存性反転の原則は以下のように定義されているらしいです(『Adaptive Code』からの引用)。

・上位のモジュールを下位のモジュールに依存させるべきではない。どちらも抽象化に依存すべきである。
・抽象化は詳細に依存すべきではない。詳細は抽象化に依存すべきである。

「なんじゃこりゃあ! 意味わからんのじゃボケェ!」と感じる方が多いかなと思います。
僕もぶっちゃけ今でもわかっているかどうかわかりませんが、ここは卑近な例を一つ取って理解してみたいと思います。

上位のモジュールは下位のモジュールに依存させるべきではない

例えば僕は株式会社HogeXerの社員だとします。
株式会社HogeXerモジュールは下位のモジュールに「ツミオ」という社員を保持していることになります。
しかし上位モジュールである株式会社HogeXerが下位モジュールである「ツミオ」に依存しているとどうなるでしょうか?
僕が気まぐれで「働くの辞めたぜ! ほなサイナラ!」なんて言った瞬間、株式会社HogeXerは僕に依存しているため、会社が機能しなくなります。
「会社が機能しなくなる」が言い過ぎな場合だとしても、依存しているからには何らかの弊害が発生するはずです。
少なくとも、会社の構造を変化させたり、やっている仕事の一部を削ったりしなければならないはずです。
これが「上位のモジュールは下位のモジュールに依存させるべきではない」という意味です。
考えてみればごく当たり前のことですね。

また、仕事を辞めるまでとは言わずとも、下位モジュールである「ツミオ」が鬱になり、一部の仕事が完全にできなくなったとしましょう。
この場合も上位モジュールである株式会社HogeXerは「ツミオ」に依存しているため、やはり会社の構造を変化させたり、やっている仕事の一部を削ったりしなければなりません。

どちらも抽象化に依存すべきである

さて次はどうでしょう。
「どちらも抽象化に依存すべきである」の「抽象化」はプログラミングで言うなら「インターフェイス」に置き換えてよいと思います。
これも引き続き先の例(会社)に倣って考えてみましょう。

抽象化に依存すべきとは、例えばここで言うならば具体的なメンバーである「ツミオ」に依存するのではなく、抽象的な「株式会社HogeXerの社員」に依存しよう、ということです。
しかしプログラミングの観点で言えば、「社員」ではまだ抽象化が不十分です。
例えば「ローカライズプログラマ」に抽象化でき、以下の要件(インターフェイス)が求められるかもしれません。
「TOEIC800点相当の英語力」「プログラミングの実務経験が2年以上」「大卒以上」
こういったものを作っていき、「社員」よりも細かく抽象化していきましょう。
そして「ツミオ」に依存する代わりに、「TOEIC800点相当の英語力」「プログラミングの実務経験が2年以上」「大卒以上」の要件を満たすメンバー(ローカライズプログラマ)に依存するのです。
もちろん、サウンドプログラマなら例えばこれが「プログラミングの実務経験が2年以上」「大卒か音楽専門卒」などに変わるでしょう。

そして、「ツミオ」というメンバーも抽象化に依存しなければなりません。
すなわち「ローカライズプログラマ」や「サウンドプログラマ」などの要件に依存するのであって、個々の会社には依存しません(株式会社HogeXerから株式会社FooBarに転職することを考えてみてください)。

注*僕はニートなのでよく知りませんが、きっと会社は個々人の能力もきちんと見ていると思います。ここのは単なる例です。

抽象化は詳細に依存すべきではない。詳細は抽象化に依存すべきである

これはまだ理解が完全ではないのですが、インターフェイスを使えば自ずと達成されるのではないかなと思っています。
例えば「TOEIC800点相当の英語力」は「ツミオ」に依存しませんよね。
また別の「社員Aさん」にも依存していません。
ツミオや「社員Aさん」という詳細が「TOEIC800点相当の英語力」に依存しているのです。
そして会社という詳細も「TOEIC800点相当の英語力」に依存していますが、「TOEIC800点相当の英語力」は会社に依存していません。

追記:補足を書きました。

実際に作ってみる

というわけで実際に作ってみましょう。
例のごとくコンソールアプリです。

こちらがインターフェイスです。

IGraduatesStateは今回の依存性反転の原則にはあまり関係がなく、GoFのステートパターンに従って使用する予定のものでした(が、コードをシンプルにしようと思い直したので中途半端な実装で終わってます)。
インターフェイスとにらめっこしていても何も始まらないので、さっさと次に行きましょう。

こちらがIGraduatesStateを実装したクラス一覧です。

まあ難しくないと思います。

そして今回メインとなる具象クラス一覧がこちら。
これらのクラスが先ほど作成したインターフェイスを実装していて、下位モジュールに相当します。

色々なクラスが好き勝手にインターフェイスを実装していますね。
そこだけ確認できればOKです。

最後は上位モジュールに相当するHogeXerCompanyクラスを見ましょう。
このクラスには具象クラスは一切登場せず、インターフェイス(あるいは抽象クラス)に依存しています。

EntryPoint.csはその名の通りエントリポイントですので、適切なクラスで実装してください。
このエントリポイントには具象クラスの名前が出てきていますね。

実行結果はこんな感じです。

例の通りには作っていませんが、依存性反転の原則に従って作られていることがわかっていただけるのではないでしょーか。
この依存性反転の原則に従うメリットは、言うまでもなく拡張性が増すことです。

HogeXerCompanyは社員という存在および使用したいインターフェイスは知っていますが、具体的に社員がどこの誰べえなのかは知りません。
会社が興味があるのは具体的な個々人そのものではなく、個々人がなんの技能を(すなわちインターフェイスを)持っているかということです。
なので既存の社員と違った技能(インターフェイス)を持つ社員を新たに雇うことも簡単にできますし、その社員は完全に他の社員と同様に扱うことができます。
また、HogeXerCompanyは個々の社員に依存していないため、個々の社員の実装が変わったとしても(例えば実装しているインターフェイスが減ったとか)、HogeXerCompanyはコードを何も変更する必要はありません。
これはHogeXerCompanyにとってはもちろんいいことですし、社員側のクラスにとってもいいことです。
というのも、社員側が「会社側のクラスを破壊してしまうかもしれない」なんてことを考える必要がなくなるからです。好き勝手に実装を変えることができちゃいます。
また、HogeXerCompanyではない別の会社のクラスを作成したとして、そのクラスで社員クラスを使用したい場合も、具象クラスではなくインターフェイスに依存してさえいれば、各々のクラスは独立性を確保できます。
いやあ便利ですね!

注1*実際の開発で個々の人間一人ひとりに相当するようなものを個別のクラスで扱うことは少ない気もしますが、ここは説明にそった例ということで。

注2*「僕もぶっちゃけ今でもわかっているかどうかわかりません」と最初に書いた通り、ここでの説明が間違っている可能性もあります。お気をつけください。

ところで全然関係ないのですが、実際の会社って個々人に依存しているところが多いイメージなんですが、本当のところはどうなんでしょうね。
この依存性反転の原則に従えばあんまりよろしくない気がしますが、あくまでもソフトウェア開発の話ですし、どうなんですかねえ。
ちゅーわけで誰か僕を雇ってください。

終わりに

本を読んだり色んなサイトで調べたりして、適応力のあるコードというのがどういったものなのか、ぼんやりとわかってきました。
ただ『Adaptive Code』は僕が読むにはまだ早かったらしく、理解できない箇所もチラホラあります。
残りは12章と13章だけなのですが、依存性の注入は以前から興味があったものなので今から読むのが楽しみです。

ほなそんな感じでまた。

フォローする