<輪読会> オブジェクト指向実践ガイド -第3章 依存関係を管理する-
輪読会メンバー
- Izumi Haruya
- Sekine Yutaro
第3章依存関係を管理する
オブジェクトに望まれる振る舞い
- オブジェクト自身が知っている
- 継承している
- メッセージを理解するほかのオブジェクトを知っている
ここで言う振る舞いは、クラス自身が独自に実装すべき振る舞いです。 2つ目の、振る舞いの継承については「第6章継承によって振る舞いを獲得する」で扱います。 この章では、3つ目の、振る舞いがほかのオブジェクトに実装されているときに、それにアクセスすることについて論じましょう。
3.1依存関係を理解する
一方のオブジェクトに変更を加えたとき、他方のオブジェクトも変更せざるを得ないおそれがあるならば、片方に依存しているオブジェクトがあります。
依存関係を認識する
以下の時に依存関係があると考える。
- ほかのクラスの名前(名前から連想される他のクラスがある時)
- そのクラスの中に他のクラスのメソッドがあることがわかる時
- メッセージが要求する引数とそれら引数の順番を考える時
上記があるときは、不必要な依存を生む。これが元に合理性を損なう。
上記はない方がいい。
オブジェクト間の結合(CBO:CouplingBetweenObjects)
依存関係は管理が難しい
- 1つのエンティティが依存関係に寄って、さも複数のエンティティかのような管理が求められる
この場合、管理が難しくなるのは時間の問題
ほかの依存関係
ほかにも、依存関係に関連した一般的な問題がある
破壊的なたぐいの依存が生じる場合
- 「『何かを知るオブジェクト』を知るオブジェクト」を知るオブジェクトがあるとき、つまり、いくつものメッセージをチェーンのように繋いで、遠くのオブジェクトに存在する振る舞いを実行しようという場合に生じます。
6章で詳しく
- 「『何かを知るオブジェクト』を知るオブジェクト」を知るオブジェクトがあるとき、つまり、いくつものメッセージをチェーンのように繋いで、遠くのオブジェクトに存在する振る舞いを実行しようという場合に生じます。
大別できる依存関係
- テストがコードに依存するとコードが毎回壊れる(テストがコードに依存している)
9章で詳しく
- テストがコードに依存するとコードが毎回壊れる(テストがコードに依存している)
3.2疎結合なコードを書く
依存関係をより詳細に理解することが大事
- 依存関係を把握し、必要のない依存関係を認識し、不要な依存関係を取り除く
疎結合とは
独立・孤立
依存オブジェクトの注入
プロジェクト全体の名前の修正
- そんなに重大ではない。まだ変更は容易
ハードコーディングとは、本来プログラム中に記述すべきでないリソース(エラーメッセージなど)を、直接ソースコード中に埋め込むことである。
- クラス内のクラスは隔離し、引数で値を拾ってくる方が良い
- 依存オブジェクトの注入を使ってコードを整えることができるかどうかは、設計者が持つ次の能力に左右される。
- クラス名を知っておく責任
- そのクラスに送るメソッドの名前を知っておく責任
- どこかほかのクラスに属するものなのではないかと疑える能力
- 依存オブジェクトの注入を使ってコードを整えることができるかどうかは、設計者が持つ次の能力に左右される。
依存を隔離する
最も理想的なのは、不必要な依存はすべて取り去ってしまうことしかし、これは「技術的には」可能であるものの、現実にはおそらく不可能でしょう。すでに存在するアプリケーションに取り組んでいると、実際にどれだけ変更できるかには厳しい制約がある、とわかることがあります。コードを完璧にできないのであれば、その次に目標とすべきは現状の改善です。完璧さは手に入れられないとしても、最初に見つけたときよりもコードをより良いものにしていくことによって、全体の状況を改善することはできます。
依存は、クラスの単一責任的な感じで隔離するべき
インスタンス変数の作成を分離する
- initializeメソッドで隔離する
- メソッド化して、独自に明示的に定義する
依存関係を隠蔽するのではなく、明示的にする
脆い外部メッセージを隔離する
外部的な依存を取り除き、専用のメソッド内にカプセル化するのが大事
- 引き算的な考えが大事
引数の順番の依存を取り除く
- 引数の渡す順番は、初期段階では足したり引いたり、デフォルト値を設定したりとサイクルを繰り返すこともあり、固定化された順番の引数を使うとそのサイクルを回すたびに修正することがある。
- これらが残ったまま大規模化すると、引数に変更を加えること自体を避けるようになる。
初期化の際の引数にハッシュを使う
ハッシュを受け取るようにコードを変えると良い。以下ハッシュのメリット
- 順番に依存せず、名前(キー)で値(バリュー)を扱える
- 名前で管理できている事で、メンテナンス性も上がる
- 固定順とハッシュの組み合わせも併用でき、扱いやすい
明示的にデフォルト値を設定する
args[:boolen_thing] || true
明示的なデフォルト値を設定する方法は以下
- ||メソッドを使用する
- nilやfalseを使用したい場合はfetchメソッドを使用する
- さらに複雑なデフォルト値を設定したい場合はmergeメソッドを使用して、メソッド化する
複数のパラメーターを用いた初期化を隔離する
複数のパラメーターを用いた初期化を隔離する方法は以下
- 他のオブジェクトを作成する事が目的のオブジェクトはファクトリーである
- モジュール化することで、作り手の意図も伝わる
- 固定順の引数をオプションハッシュに置き換えるのは、自分で変更がきかない外部のインターフェースに依存せざるを得ない場合に適している
3.3依存方向の管理
依存関係には方向があり、逆にしたら良い
依存関係の逆転
依存関係の方向が、未来に生じるであろう変更に大きな影響がある
依存方向の選択
少しの間、クラスがあたかも人間であるかのように考えてみましょう。彼らの振る舞い方にアドバイスをするとすれば、きっと、「自身より変更されないものに依存しなさい」とアドバイスをするのではないでしょうか。
依存方向を考える上で、考慮すべき点
- あるクラスは、ほかのクラスよりも要件が変わりやすい
- 具象クラスは、抽象クラスよりも変わる可能性が高い
- 多くのところから依存されたクラスを変更すると、広範囲に影響が及ぶ
変更の起きやすさを理解する
変更の置きやすさに重心を置く
- 変更の頻度を、順位付けして管理することが大事
具象と抽象を認識する
- コードの具象性と抽象性を考えるのが大事
- 静的型付け言語では、型付けの際に意図が見える
- 動的でも、設計の際には「インターフェース」を考慮すべき
インターフェースとは、型付けのことではないか(問い)
大量に依存されたクラスを避ける
大きいクラスとの依存関係をなくすことが大事
問題となる依存関係を見つける
A:クラスが抽象になるから依存関係が増える
B:中立的である
C:中立的である
D:悪魔のような関係
依存関係を減らすことが何より大切である
- 「自分より変更されないものに依存しなさい」
3.4まとめ
依存関係の管理は、将来のアプリの核となる
- 特に、鍵は依存関係の方向を制御すること
- 自身より変更の少ないクラスに依存させること
参考書籍
オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方 | Sandi Metz, 髙山泰基 | 工学 | Kindleストア | Amazon