制御結合
結合度の一種。呼び出し元がフラグやモード値を渡し、呼び出し先がその値によって処理分岐を変えるパターン。
問題点
- 呼び出し側が分岐ロジックを知る必要がある(暗黙の知識依存)
- 複数モードの組み合わせ理解が必要
- モード追加時の保守性低下
- 一つの関数が複数の責務を持つ(凝集度の低下)
本質:制御所有の位置
制御結合の問題はif文の存在ではなく、制御所有の位置にある。
// 悪い例:呼び出し側が分岐を制御
function process(data: Data, mode: "sync" | "async") {
if (mode === "sync") { /* ... */ }
else { /* ... */ }
}
process(data, "sync"); // 呼び出し側が内部実装を知っている
解決策:ポリモーフィズム
ポリモーフィズムを使うと、制御の責任が「呼び出し」から「構築」に移動する。
// 良い例:構築時に型を決定
interface Processor { process(data: Data): void; }
class SyncProcessor implements Processor { /* ... */ }
class AsyncProcessor implements Processor { /* ... */ }
const processor = new SyncProcessor(); // 構築時に決定
processor.process(data); // 呼び出し時はフラグ不要
判別共用体との関係
判別共用体のtype属性は、使い方によって制御結合にもデータ結合にもなりうる:
- 命令的フラグ(悪い):バックエンドが実装詳細を操作
- 識別子(良い):データの属性を宣言的に表現
境界層(腐敗防止層)で一度だけ分岐を吸収し、内部は宣言的に実装するのが現実的な解決策。
関連
参照
- 結合とコナーセンスに関する考察 - 制御結合の問題点と解決策の詳細な考察