疎結合とは?

公開日:
最終更新日:

イントロダクション:疎結合を一言で言うと?🔗

疎結合(そけつごう)とは、「お互いに強く依存しすぎないように部品同士をつなぐこと」です。

あるクラスやモジュールを変更しても、他の場所への影響ができるだけ小さくなるようにする考え方で、 「変更に強い設計」を支える、とても大事な基礎用語です。

1. なぜ疎結合が大事なの?(現場でよく効く理由)

疎結合になっていると、システムをあとから変更したり、部品を差し替えたりするときに、とても動きやすくなります。

逆に、部品同士がベッタリつながった「密結合」のままだと、次のような困りごとが起きやすくなります。

  • ちょっとした修正で壊れやすい
    あるクラスを直しただけなのに、思わぬ別の機能まで一緒に壊れてしまう。
  • テストしにくい
    1つのクラスをテストしたいだけなのに、他のクラスや外部サービスが一緒に動いてしまい、単体テストが書きづらい。
  • 置き換え・差し替えが難しい
    DBや外部API、UIなどを変えたいときに、あちこちのコードを書き換えないといけなくなる。

疎結合を意識して設計しておくと、 「変更しやすい」「テストしやすい」「長く育てやすい」 システムに近づいていきます。

2. 疎結合と密結合の違い

ざっくりとしたイメージとして、次のように考えると分かりやすくなります。

  • 密結合:
    クラスAがクラスBの「具体的な実装」にベッタリ依存している状態。
    Bの中身や使い方が変わると、すぐAも壊れてしまう。
  • 疎結合
    AはBの「インターフェース(外から見える約束ごと)」だけを知っていて、
    中身の細かい実装にはなるべく依存しないようにしている状態。

現場では、 「この2つ、ちょっと密結合すぎない?」 といった会話で使われることが多いです。 「お互いを知らなさすぎて困る」のではなく、 「ほどよい距離感でつながっている」 ことがポイントです。

3. 疎結合に近づけるための工夫 🧱

疎結合にするためのテクニックはいくつかありますが、代表的なものをいくつか挙げます。

インターフェースや抽象クラスをはさむ

具体的なクラスどうしを直接つなぐのではなく、 「このメソッドが呼べればOK」という抽象的な窓口 を用意してつなぐ方法です。実装を差し替えたいときも、そのインターフェースを満たすクラスを用意するだけで済みます。

依存の向きをそろえる(依存方向を意識する)

ビジネスロジックが、UIやDBなどの「外側の都合」に引きずられないように、 中心にあるルールほど、外側の詳しい仕組みに依存しない ようにします。 依存の向きをそろえておくと、設計が整理されて見通しも良くなります。

コンストラクタやDIコンテナで依存を渡す

クラスの中で直接 new してしまうのではなく、 必要な相手は外から注入してもらう(依存性の注入:DI) ようにすると、テスト用のダミーや別実装に差し替えやすくなります。

どのテクニックも共通して、 「片方を変えても、もう片方への影響は最小限にしたい」 という目的に向かっています。

図解で見る「密結合」と「疎結合」

疎結合と密結合の違いを、UI・実装A / 実装B・DB の4つのモジュールで図解します。 同じ登場人物でも、結合のさせ方によって変更のしやすさ / 壊れやすさが大きく変わります。

疎結合は、モジュール同士がお互いの詳細をあまり知らず、 必要最小限の情報だけでゆるくつながっている状態です。 一方で密結合は、モジュール同士が直接ベタベタにつながり、 どこか1か所を変えると周りも巻き込んで壊れやすい状態を指します。

密結合の構成例 密結合 UI 実装A 実装B DB
UI・実装A・実装B・DB が互いに直接参照し合い、矢印だらけでベタベタにつながった状態。 どこか1つを変えると、他のモジュールも連鎖的に修正が必要になりやすい構成です。
疎結合の構成例 疎結合 UI インターフェース 実装A 実装B DB
UI はインターフェースにだけ依存し、実装A/B と DB はインターフェース経由でゆるくつながる構成。 誰か1つの詳細を変えても、他のモジュールへの影響を小さく保ちやすくなります。

JavaScriptで見る「密結合」と「疎結合」の例

ユーザー登録時の「お知らせ送信」を題材に、フォームやメール送信の具体実装にベッタリ依存した密結合の例と、 インターフェースで責務を分離した疎結合の例を比較できるコードスニペットです。※コードのコメントを読むだけでも理解できるかと思います。

密結合の例:具体的なメール送信処理にベッタリ依存するコード

フォームDOMの扱い・ユーザー作成・メール文面の組み立て・送信APIの呼び出しまで、 1つの関数&特定のモジュールにくっついている例です。 通知方法を変えたいだけでも大きな修正が必要になります。

// 密結合の例:登録とメール送信がベッタリくっついている

// メール送信モジュール(具体的なAPIに直結している)
const emailSender = {
	send(to, subject, body) {
		// 実際には fetch や外部サービスSDKなどを叩く想定
		console.log('メール送信:', { to, subject, body });
		// fetch('/api/send-email', { ... }) のようなコードがここに来る
	}
};

// フォーム送信イベントから直接呼ばれるハンドラ
function registerUserTightlyCoupled(formElement) {
	// 1. フォームから直接DOM操作で値を取得
	const name = formElement.querySelector('[name="name"]').value;
	const email = formElement.querySelector('[name="email"]').value;

	// 2. ユーザー作成ロジック(ここではローカルストレージに保存)
	const users = JSON.parse(localStorage.getItem('users') || '[]');
	const newUser = {
		id: users.length + 1,
		name,
		email,
		createdAt: new Date().toISOString()
	};
	users.push(newUser);
	localStorage.setItem('users', JSON.stringify(users));

	// 3. メール文面の組み立てもここで実施
	const subject = 'ようこそ!' ;
	const body =
		newUser.name + ' さん、登録ありがとうございます!\n' +
		'あなたの会員IDは ' + newUser.id + ' です。';

	// 4. 特定のメール送信モジュールにベッタリ依存
	emailSender.send(newUser.email, subject, body);

	// 5. UI 更新もここでやってしまう
	const message = document.getElementById('tightlyCoupledMessage');
	message.textContent = '登録とメール送信が完了しました(密結合の例)';
}

疎結合の例:登録処理と通知処理をインターフェースで分離

「ユーザーを登録する処理」と「登録されたことを通知する処理」を切り離し、 通知側はインターフェース(契約)で受け取るようにした例です。 メール以外の通知方法に差し替えるのも簡単になります。

// 疎結合の例:登録処理と通知処理をインターフェースで分離

// ユーザーリポジトリ:保存だけに責務を限定
const userRepository = {
	save(userInput) {
		const users = JSON.parse(localStorage.getItem('users') || '[]');
		const newUser = {
			id: users.length + 1,
			name: userInput.name,
			email: userInput.email,
			createdAt: new Date().toISOString()
		};
		users.push(newUser);
		localStorage.setItem('users', JSON.stringify(users));
		return newUser;
	}
};

// 通知インターフェースを「満たす」オブジェクトの例1(メール通知)
const emailNotifier = {
	notifyUserRegistered(user) {
		const subject = 'ようこそ!';
		const body =
			user.name + ' さん、登録ありがとうございます!\n' +
			'あなたの会員IDは ' + user.id + ' です。';
		console.log('メール通知:', { to: user.email, subject, body });
	}
};

// 通知インターフェースを「満たす」オブジェクトの例2(コンソール通知)
const consoleNotifier = {
	notifyUserRegistered(user) {
		console.log('コンソール通知: 新規ユーザー登録', user);
	}
};

// 疎結合なユースケース:
// 「ユーザーを登録して、通知を飛ばす」という振る舞いだけを担当
function registerUser(userRepo, notifier, userInput) {
	const user = userRepo.save(userInput);
	// notifier が notifyUserRegistered(user) を持っていることだけを契約とする
	notifier.notifyUserRegistered(user);
	return user;
}

// UI 層:フォームとユースケースをつなぐ薄い部分
function handleRegisterUserLooselyCoupled(formElement) {

	// フォームから値を取得
	const userInput = {
		name: formElement.querySelector('[name="name"]').value,
		email: formElement.querySelector('[name="email"]').value
	};

	// ユーザーを登録して、通知を飛ばす ※ここで自由に通知機能を差し替え可能に
	registerUser(userRepository, emailNotifier, userInput);

	// 通知
	const message = document.getElementById('looselyCoupledMessage');
	message.textContent = '登録と通知が完了しました(疎結合の例)';

}

まとめ:疎結合を意識するときのポイント

  • 疎結合とは、部品どうしが強く結びつきすぎないように設計する考え方です。
  • 疎結合になっていると、変更・テスト・差し替えがしやすいシステムに近づきます。
  • インターフェースをはさむ、依存の向きをそろえる、DIで依存を注入する…といったテクニックは、 疎結合に近づけるための道具として使われます。

まずは「このコード、もう少し疎結合にできないかな?」と考えてみることが、 設計の質を一歩ずつ上げていくための練習になります。

このページの著者

もちもちみかん(システムエンジニア)

社内SEとしてグループ企業向けの業務アプリを要件定義〜運用まで一気通貫で担当しています。

経験:Webアプリ/業務システム

得意:PHP・JavaScript・MySQL・CSS

個人実績:フォーム生成基盤クイズ学習プラットフォーム

詳しいプロフィールはこちら!  もちもちみかんのプロフィール

もちもちみかん0系くん
TOPへ

もちもちみかん.comとは


このサイトでは、コーディングがめんどうくさい人向けのお助けツールとして、フォームやCSSをノーコードで生成できる、
 もちもちみかん.forms
 もちもちみかん.css1
 もちもちみかん.css2
と言ったジェネレーターを用意してます。

また、このサイトを通じて、「もちもちみかん」のかわいさを普及したいとかんがえてます!