依存方向の管理
依存関係には常に方向がある。
つまり、AはBに依存しているようなアプリが、BがAに依存するように変更がある可能性もあるということ。
ここで、クラスを人と見立ててアドバイスするなら、__「自身より変更されないものに依存しなさい」__とアドバイスすること。
この一文を詳細に説明すると、
・あるクラスは、他のクラスよりも要件が変わりやすい
・具象クラスは、抽象(abstract)クラスよりも変わる可能性が高い
・多くのところから依存されたクラスを変更すると、広範囲に影響が及ぶ
ここでの抽象(abstract)とは、「いかなる特定の実例(インスタンス)からも離れている」ということ。
(クラス内で別のクラスを呼んだりしていることは、特定の実例に近すぎる一例。)
変化のおきやすさを理解する
プログラミング言語の基本的なクラスは変わりにくい。だが、フレームワークを考えるとどうか。
一般的なフレームワークを使用するにおいては、自前のコードよりも安定していることが多い。
だが、発展途上のフレームワークを使っていて、それの更新が頻繁に起こる可能性は当然ありえる。
つまり、アプリ内のクラスは、変化のおきやすさによって順位付ができる。この順位付こそが、依存の方向を決める際の一つの鍵。
具象と抽象を認識する
抽象とは、「いかなる特定の実例(インスタンス)からも離れている」ということ。
つまり、クラス内で別のクラスのインスタンスを呼ぶ行為はとても具象的だが、メソッドを細かく切り分け、結合の切り離し(依存オブジェクトの注入(dependency injection)という。)を行ったあとは抽象的なものに依存するようになる。
具象クラスは、抽象クラスよりも変わる可能性が高い
抽象インターフェースを定義する、ということを動的型付け言語で考えてみると、
クラスAをクラスBに注入することで、クラスAが(クラスBのメソッドである)メソッドBに応答するダックタイプ(型宣言しなくても、勝手にメソッドBがクラスBのものだと判断)に依存するように変える時、実は、さりげなくインターフェースを定義している。
このインターフェースは、__「あるカテゴリーのものはメソッドBを持つ」__という概念が抽象化されたもの。
抽象(あるカテゴリーがメソッドBをもつこと)が、具象クラス(クラスB)から取り出されているわけだが、これはもはや「いかなる特定の実例(インスタンス)からも離れている」もの。
抽象化されたものが素晴らしいのは、__「抽出元となった具象クラスよりも変わりにくい」__こと。
抽象化された者への依存は、具象的な者への依存よりも常に安全。
大量に依存されたクラスを避ける
・多くのところから依存されたクラスを変更することによる影響は明白
・明白ではないのは、「多くのところから依存されたクラス」を「持つこと」自体の影響。
そのような影響力の大きいクラスを持った時点で、アプリは一生ハンディキャップを抱える可能性もある。
A - 抽象クラスやインターフェース
抽象化されたものは変更が起きにくいので、依存はそこに集まる
注意すべきことは、領域Aにあるからって、クラスが抽象になるわけではない。クラスが「すでに」抽象であるからこそ、領域Aに収まる。それらが備える抽象の性質によって、クラスはより安定したものになり、多数の依存を安全に引き受けることができる。(そのクラスが抽象クラスであるべきだということを示しているのは確か。)
B - 設計時に最も考慮する必要のないもの。
C - コードは変わりやすく、依存はわずかしかない。傾向として具象的なクラスが集まる。この場合変わりやすさは問題ではない。
D - 危険領域。変更が約束され、依存もたくさんあるものがここに入ってしまう。具象クラスが多量の依存関係を持っていたら注意するべき。
このクラスが表しているのは、アプリケーションの将来的な健康状態への危険。さらにもっと危険な状態もあり、「領域Dのクラスを、そこに依存しているクラスよりさらに変更されやすくする」ことで、全ての変更が最大化されてしまう。
こういったことをなくすためにも
・具象ではなく、抽象なオブジェクトに依存させる
・具象なもの同士が絡み合っている場合は、積極的に依存オブジェクトの注入を行う。
・依存関係の方向を制御すること。(自分より変更されないもの、つまり抽象オブジェクトに依存すべき。)