概要
JavaScriptはクラスベースのOOPも、関数による構成的設計も許容する言語である。
だからこそ重要になるのが、**「どちらをいつ選ぶか」**という戦略的判断だ。
クラスは状態管理と継承構造を持ちやすく、関数はテスト容易性と柔軟性に優れる。
本稿では、クラスと関数の設計トレードオフ、選定基準、状態・継承・副作用への適応戦略を整理し、設計の観点から体系的に比較する。
1. クラスベース設計の特徴
class User {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}`;
}
}
const user = new User('Taro');
user.greet();
- ✅ 状態を持つインスタンスの生成に適する
- ✅ 継承・ポリモーフィズムを使いたいときに明示的
- ❌ 継承のツリーが複雑化しやすく、柔軟性に欠ける
2. 関数ベース設計の特徴
function createUser(name) {
return {
greet: () => `Hello, ${name}`
};
}
const user = createUser('Taro');
user.greet();
- ✅ 閉包による状態管理が可能
- ✅ 柔軟・軽量・依存が明示的 → テスタブルな構造
- ❌ 明示的な型/継承階層を設計するには工夫が必要
3. 継承 vs コンポジションの選択基準
// 継承(OOP)
class Animal {
speak() {
return '...';
}
}
class Dog extends Animal {
speak() {
return 'ワン!';
}
}
// コンポジション(関数構成)
function speakDog() {
return { speak: () => 'ワン!' };
}
function createDog() {
return {
...speakDog(),
name: 'Pochi'
};
}
戦略 | 特徴 |
---|---|
継承 | 型階層が明確、IDE支援が効く |
コンポジション | 責務ごとの構成、柔軟性・再利用性が高い |
- ✅ 単純な型差異だけなら継承、
- ✅ 複数の機能合成や差し替えが必要ならコンポジションが適する
4. 状態と副作用に対する設計視点
クラス:状態を内部に閉じ込めやすい
class Counter {
#count = 0;
increment() {
this.#count++;
return this.#count;
}
}
関数:状態を引数で受け渡し、副作用を分離可能
function increment(count) {
return count + 1;
}
- ✅ 関数ベースは状態の責任を明示的に制御できる
- ✅ クラスは状態と振る舞いが一体化するため隠蔽性に優れる
5. 実装の適用指針
観点 | クラス設計が有利な場面 | 関数設計が有利な場面 |
---|---|---|
状態管理 | 複数のプロパティをまとめて保持したい | 状態は外部管理、関数の引数で制御したい |
継承構造 | 型ごとにメソッドを上書きしたい | ツリー構造を避け、構成で差し替えたい |
テスト性 | モック可能な外部依存を注入しづらい | 関数単位で依存を切り替えやすい |
柔軟性 | 型と振る舞いを密接に設計したい場合 | ユースケース単位で自由に構成したい場合 |
設計判断フロー
① 継承関係が必要か? → ないならコンポジションを優先
② 状態を内部に持たせたいか? → 必要ならクラスで一元化
③ 単体テストしやすいか? → 関数分離で依存を注入可能に
④ 振る舞いの差し替えはどこで必要か? → コンポジションで責務分離
⑤ 柔軟性と保守性のバランスを取りたいか? → 関数型 + DI戦略を導入
よくあるミスと対策
❌ 継承ツリーが深くなり、どこでオーバーライドされたか不明
→ ✅ 委譲(コンポジション)で構成し、責務ごとに分割する
❌ 関数ベースで状態をもたせすぎて密結合に
→ ✅ 外部状態に依存する部分は外へ出し、副作用を注入形式にする
❌ クラスに大量のユーティリティ関数を持たせて単一責務が崩壊
→ ✅ 責務ごとにクラス or ファクトリを分離
結語
クラスと関数、どちらを選ぶかは「好み」ではない。
それは**“状態・振る舞い・責任の所在をどう構造化し、どこまで柔軟性を持たせるか”という設計の選択**である。
- クラス:状態と振る舞いの一体化、明確な型設計
- 関数:柔軟性とテスト性、責務の明示と切り替え可能性
- 両者を使い分け、アーキテクチャに応じた設計戦略を選択することが鍵
JavaScriptにおける構造設計とは、
“構文ではなく責務と変化耐性を設計することで、進化に耐えるコードを作る戦略である。”