エスプリフォートではただ技術を学ぶだけでなく、積極的に新しい事を取り入れ、保守性、可読性なども考慮した設計、プログラミングを心がけています。
これら磨きあげた技術力が、お客様に高いクオリティでシステムを提供する一助になっています。
今回は良い設計、コードとして大切な「凝集度・結合度」をご紹介いたします。
そもそも「良い設計」ってどんなもの?
良いプログラムは、
・わかりやすくて
・直しやすくて
・他の人も使いやすい
そんな設計になっています。
そのために大事なのが、
「凝集度(ぎょうしゅうど)」 と 「結合度(けつごうど)」 です。
凝集度(Cohesion)=「部屋の使い方が整理されているか」
● 例え話:家の中の“部屋”
高い凝集度の家 →
「寝室では寝る」「台所では料理する」など、部屋ごとに役割がハッキリ
低い凝集度の家 →
「寝室で料理」「お風呂で洗濯して読書もする」みたいにごちゃごちゃ
● プログラムでは?
高凝集:1つのクラス・関数が1つの目的に集中している
低凝集:いろんな目的が混ざっている
✅ 良い例(高凝集):
// 認証だけを担当するクラス
class Authenticator {
boolean login(String id, String password) {
// ログイン処理だけ
}
}
❌ 悪い例(低凝集):
// いろんなことを詰め込んでいるクラス
class UserManager {
void login(...) {...}
void sendEmail(...) {...}
void exportToCSV(...) {...}
}
● 高凝集(望ましい状態)
- モジュールが 1つの目的・責務に集中している
- メソッドやクラスが 意味的に一貫性を持っている
- 変更の影響範囲が 局所的になる(保守性向上)
● 低凝集(避けるべき状態)
- モジュールが 複数の責務を混在させている
- 一部を変更すると他の無関係な処理に影響する
結合度(Coupling)=「部屋が変にくっついてないか」
● 例え話:部屋と部屋のつながり
低い結合度 →
「寝室と台所がちゃんと分かれてる。改装もカンタン」
高い結合度 →
「台所の水道を直したら寝室の照明が消える」=変な依存関係がある
● プログラムでは?
低結合:他のクラスとゆるくつながってる → 修正しやすい!
高結合:他のクラスとベッタリくっついてる → 直すのが大変…
✅ 良い例(低結合):
// 外から必要なものだけ受け取る
class OrderService {
Payment payment;
OrderService(Payment payment) {
this.payment = payment;
}
void executeOrder() {
payment.pay();
}
}
❌ 悪い例(高結合):
// 中で直接作っちゃってる
class OrderService {
void executeOrder() {
StripePayment p = new StripePayment();
p.pay(); // Stripe専用、変更に弱い
}
}
● 低結合(望ましい状態)
- 他のモジュールへの依存が 最小限
- モジュールの変更が 他に波及しにくい
- テストや再利用がしやすくなる
● 高結合(避けるべき状態)
- モジュール同士が 直接依存しすぎている
- 小さな変更でも 大規模な修正が必要になる
実務でどう活かす?~“高凝集・低結合”がプロジェクトの命を守る~
ソースコードは動けばいい――最初はそう思いがちです。
でも、開発が進んで機能が増えるにつれて、 「保守しやすさ」や「変更への強さ」 が求められるようになります。
そこで重要になるのが、 凝集度(Cohesion)と結合度(Coupling) です。
■ 高凝集を目指す:SRP(単一責任の原則)
実務で高凝集を実現するためには、 「1クラス(または1関数)に1つの責任だけを持たせる」 という考え方が非常に大切です。これは「SRP(Single Responsibility Principle)」という設計原則で知られています。
例えば、あるクラスが「ログイン認証」「メール送信」「CSV出力」など複数の役割を持っていると、1つの機能を変更しただけで他の機能にも影響してしまいます。
→ 修正ミス・バグの温床になりがちです。
実務では、以下のように機能を分けることがポイントです:
// 責務ごとにクラスを分ける(SRPを意識)
class Authenticator { ... }
class EmailSender { ... }
class CsvExporter { ... }
これにより、コードの見通しが良くなり、テストやレビューもしやすくなります。
■ 低結合を実現する:DI(依存性注入)
一方で、クラス同士が密に結びつきすぎていると(高結合)、1つを変更すると他のクラスまで修正が必要になり、保守性が著しく低下します。
この問題を解消するのが、 DI(Dependency Injection/依存性注入) というテクニックです。
DIとは、必要な依存関係(例:支払い処理など)を外から渡す設計方法です。
🔽 DIを使った例:
class OrderService {
private final Payment payment;
// コンストラクタで外から注入
OrderService(Payment payment) {
this.payment = payment;
}
void processOrder() {
payment.pay();
}
}
このようにしておけば、あとからPaymentの中身を変えてもOrderServiceには影響がありません。
テスト時にモックの支払い処理を渡すこともでき、テストしやすくなります。
最後に
凝集度と結合度は、設計の“質”を左右する重要な指標です。
「SRP(単一責任の原則)」で高凝集を実現し、
「DI(依存性注入)」で低結合を実現することが、
結果としてバグの少ない、保守しやすい、長く使えるシステムにつながります。
新人エンジニアでも、意識するだけで設計レベルは大きく変わります。
最初は少し意識して、慣れてきたら自然に取り入れられるようにしていってみてはいかがでしょうか。
凝集度(Cohesion)が高いことによるメリット
- クラスや関数の目的がハッキリしている
- 修正や機能追加がしやすい
- 他の人が見ても理解しやすい
- 再利用しやすい
凝集度(Cohesion)が低いことによるデメリット
- 目的があいまいになりやすい
- 一部を変えると他の機能に影響しやすい
- テストやデバッグがしにくい
- 保守がどんどん難しくなる
結合度(Coupling)が低いことによるメリット
- 変更の影響が最小限で済む
- テストしやすい(モック化などが簡単)
- 再利用しやすい
- 部品ごとの独立性が高い
結合度(Coupling)が高いことによるデメリット
- 変更が他に連鎖する
- 他のクラスが壊れやすい
- テストしにくい(依存先が多い)
- 再利用しにくい