第8章 密結合 ─ 絡まって解きほぐせない構造 ─
ソフトウェア設計において、モジュール間の依存関係の強さは「結合度」として表される。結合が強すぎると、一部を変更するだけで他の部分にまで影響が及び、保守性が著しく低下する。この章では、密結合の問題と、それを解消するための設計手法について学ぶ。
8.1 密結合と責務
密結合の典型的な例として、以下のようなクラス構造が挙げられる。
- 一部のクラスに処理が集中している
- 何も処理を持たないクラスが存在する
- 他のクラスの一部のメソッドを無理やり流用している
これは単一責任の原則 (Single Responsibility Principle; SRP) に違反している状態であり、様々な問題を引き起こす。
単一責任の原則 (SRP)
SRP とは、「クラスが担う責任は、たったひとつに限定するべき」という原則である。この原則を守らないと、次のような問題が発生する。
- 偶然同じ処理だからと使い回すと、意図しない責任の重複が発生する
- データだけを持ち、処理を持たないクラスは、そもそも責務を果たしていない
- なんでもやるクラスを作ると、コードの重複が増え、変更が困難になる
「Manager クラス」を安易に作らず、一つのことに集中する Value Object を設計することが重要だ。共通化の判断は慎重に行うべきであり、異なる概念を無理に DRY (Don't Repeat Yourself) にしないよう注意する。
実際の開発でも、共通化したつもりが後々異なる概念へと分岐し、結果的に無理な条件分岐を増やしてしまうケースは多い。仕様変更の際には、過去に共通化した部分が本当に正しいのかを再評価し、場合によっては「共通化を解く」ことも視野に入れるべきだ。
8.2 密結合の各種事例と対処方法
継承による密結合
継承 (is-a 関係) は便利な機能だが、強い依存関係を生み出すため、推奨されない。
- サブクラスはスーパークラスの構造に依存し続ける
- スーパークラスの変更によって、サブクラスの動作が壊れやすい
- 共通ロジックの置き場としてスーパークラスを利用すると、仕様変更の際に不自然な構造になりがち
そのため、継承よりも コンポジション (has-a 関係) を優先するべきである。スーパークラスがサブクラスの存在を意識せずに済む設計のほうが、影響範囲を限定できる。
開発の現場でも、継承を使った設計が後に足枷になることは多い。共通ロジックを持たせたいときは、まずは 値オブジェクトやコンポジション を検討し、それでも難しい場合に継承を考えるようにするべきだろう。
クラス分割による密結合の解消
一つのクラスが複数の異なる責務を持っている場合、そのクラスは分割すべきである。特に、
- メソッドが互いに無関係なインスタンス変数を使用している
- private メソッドが異常に多い
このような状況では、責務の分離を行い、それぞれの関心事を独立したクラスにするのが望ましい。
現場では、つい private メソッドを増やしてしまいがちだが、それは往々にして「クラスが本来持つべきでない責務を抱え込んでいる」証拠である。Controller クラスなどは特に責務が集中しやすいため、意識的に整理することが重要だ。
アクセス修飾子の適切な使用
何でも public
にしてしまうと、影響範囲が広がり、密結合を助長する。Java では package private
(デフォルトのアクセス修飾子) によってパッケージ内の依存を制御できるが、PHP ではこの仕組みがないため、特に注意が必要だ。
また、public メソッドを増やしすぎると、外部のクラスからの過剰な依存を生みやすくなる。内部で完結すべきロジックは、適切にカプセル化するべきだ。
高凝集の誤解と密結合
高凝集を意識するあまり、異なる概念のロジックを一箇所にまとめてしまうことがある。結果として、コードの可読性が低下し、修正が困難になる。
- 概念が異なるものは別のクラスにする
- ある概念の値を使って別の概念の値を算出する場合は、適切なコンストラクタを用意する
このような原則を意識し、適切なクラス設計を心がけることが大切だ。
8.3 密結合の具体例
密結合が発生しやすい典型的なケースを紹介する。
- スマート UI:UI 層に計算や分岐ロジックが実装されている
- 巨大データクラス:すべてのユースケースで扱うデータクラスを定義し、結果的にグローバル変数のように扱われる
- トランザクションスクリプト:長大なメソッドの中で手続き的な処理がダラダラと続く
- 神クラス:責務の集約が進みすぎて、あらゆる処理を担う巨大クラスが誕生する
これらを防ぐには、クラスを適切に分割し、単一責任を守ること が最も重要である。一般的に、適切に分割されたクラスのサイズは 100~200 行程度になる。
まとめ
密結合は、ソフトウェアの柔軟性を大きく損なう要因となる。本章で述べたように、継承を避けてコンポジションを使う、単一責任を守る、適切なクラス分割を行う ことで、密結合の問題を解消できる。
実務では、つい短期的な実装のしやすさを優先してしまいがちだが、その積み重ねが将来の技術的負債につながる。本章で学んだ原則を意識しながら、日々の設計を見直していくことが重要だ。