コードは動くのに、機能を一つ直すだけで、なぜあちこち修正が必要になるのでしょうか。
ボタンの色を変えただけで決済ロジックが壊れる経験、誰にでも一度はありますよね。
その原因をたった二つの言葉で説明する概念が、結合度と凝集度です。
結合度は低いほどよく、凝集度は高いほどよい。よい設計の大半は、この一文から始まります。
この記事では、二つの概念の正確な意味、設計原則の根幹と呼ばれる理由、実際のコードへの適用方法まで、私の経験を交えて解説します。
まずは要点を整理しておきましょう。
- 結合度(Coupling): モジュール同士がどれだけ絡み合っているか → 低いほど よい
- 凝集度(Cohesion): モジュール内のコードが一つの仕事にどれだけ集中しているか → 高いほど よい
- この二つが、SOLIDやデザインパターンなど、有名な原則の土台です。
- 目標は一つ、「直しやすく、差し替えやすいコード」です。
結合度と凝集度とは、一体何でしょうか?
まず、二つの概念を一文でまとめるとこうなります。
結合度はモジュール間の関係を、凝集度は一つのモジュール内部のまとまりを指します。
方向が正反対なので混同しやすいですが、私はこう覚えました。
結合度は「外との距離」、凝集度は「内側のまとまり」です。
結合度が高いと、Aを直すだけでBやCまで次々と影響を受けます。ドミノのように。
凝集度が低いと、一つのクラスに決済ロジック、メール送信、ログ記録が混在します。名前だけでは何をするクラスか分かりません。
よい設計は、この二つを逆方向に進めます。外とは疎結合に、内側は強固に。
なぜこれが設計原則の「根幹」なのでしょうか?
結合度と凝集度は、1970年代の構造化設計理論で初めて整理された概念です。ラリー・コンスタンティンが提唱したとされています。
半世紀近く経っても通用するのは、特定の言語や流行に左右されない本質だからです。
よく知られた原則を掘り下げると、結局ここに行き着きます。
| 原則・パターン | 結局伝えたいこと |
|---|---|
| 単一責任原則(SRP) | 凝集度を高める |
| 依存性逆転(DIP) | 結合度を下げる |
| インターフェース分離(ISP) | 不要な結合を断つ |
| 大半のデザインパターン | 低い結合度 + 高い凝集度 |
分かりますよね。名前は違っても、根は一つです。
だから新しい原則やパターンを学ぶときは、「結合度を下げるものか、凝集度を高めるものか」とまず考えます。これで半分は理解できます。
結合度を下げる方法、コードで見ると?
言葉だけでは抽象的なので、短い例を見てみましょう。
以下は結合度の高いコードです。注文クラスが特定の決済会社を直接知っています。
class Order {
let pay = KakaoPay() // Kakao Payにべったり依存
func checkout() {
pay.send() // 別の決済に変えるならここを大改修
}
}
決済会社をTossに変える瞬間、Orderを開いて修正する必要があります。これが高い結合度の典型です。
次はプロトコルで間に距離を作ってみます。
class Order {
let pay: Payment // '「決済」という契約だけに依存
init(pay: Payment) { self.pay = pay }
func checkout() { pay.send() } // 何が来ても関係ない
}
これでOrderは、どの決済会社が来ても気にしません。差し替えるだけです。
実務でこう変えてからは、決済会社の追加依頼が来ても既存コードをほとんど触らなくなりました。これが低い結合度の力です。
凝集度を高めるにはどうすればよいでしょうか?
凝集度は、「このモジュールが一つの仕事だけをしているか」で判断します。
私の基準は単純です。クラス名を口にしたとき、その中のメソッドが自然にすべて思い浮かぶなら、凝集度は高いといえます。
たとえばUserServiceに次の処理が混在していたら、危険信号です。
- 会員登録の処理(本業)
- マーケティングメールの送信(他人の仕事)
- CSVレポートの生成(これも他人の仕事)
無関係な仕事が、一つ屋根の下で同居しているようなものです。
この場合、メールはEmailSenderに、レポートはReportGeneratorに、それぞれ部屋を与えます。
そうすれば、メールロジックを直すときにUserServiceを開く必要がなくなります。変更の波及範囲が小さくなるのです。
低い結合度と高い凝集度は、結局同じ方向を目指します。変更を広げないことです。
よくある質問 (Q&A)
Q. 結合度と凝集度、どちらから意識すべきですか?
私は、まず凝集度を整えることを勧めます。一つのモジュールを一つの仕事に集中させて分割すると、自然に結合度も整理されることが多いからです。
Q. 結合度は必ず0がよいのでしょうか?
いいえ。モジュール同士がまったく結び付かなければ、プログラムは動きません。目標はゼロではなく、「必要な分だけ、疎結合に」です。
Q. 最初から完璧に設計すべきですか?
その必要はありません。私もまず動くものを作り、直すのがつらくなるたびに少しずつ結合を断ち、凝集度を高めます。リファクタリングは本来、繰り返すものです。
結合度と凝集度は華やかな新技術ではありませんが、長く使えるコードを書く人に共通する習慣です。
今日書いたコードで「これを直すと、どこまで影響するだろう?」と一度考えてみてください。その問いを重ねると、設計感覚が大きく磨かれます。応援しています!

