概要
オブジェクト設計とは、「この塊は何を表していて、何ができるのか」を言語化する行為である。
JavaScriptでは、関数・オブジェクト・クラス・プロトタイプと複数の表現手段があり、柔軟ゆえに混乱しやすい。
だからこそ、「構造」「振る舞い」「責務」を明確に分離・設計することが、メンテナブルなコードの前提となる。
1. POJO(Plain Old JavaScript Object)による構造設計
const user = {
id: 'u01',
name: 'Toto',
isAdmin: false
};
- ✅ シリアライズしやすく、状態のスナップショットとして機能
- ✅ 特に状態管理(Redux等)やAPI通信との親和性が高い
- ❌ 振る舞い(関数)を内包し始めると構造が曖昧に
2. クラス設計:意味のある「型」として構築する
class User {
constructor(id, name, isAdmin = false) {
this.id = id;
this.name = name;
this.isAdmin = isAdmin;
}
displayName() {
return `${this.isAdmin ? '[ADMIN] ' : ''}${this.name}`;
}
}
- ✅ データと振る舞いを一つの意味としてパッケージ化
- ✅ 再利用・拡張しやすく、テスト対象としても明確
- ❌ 状態と副作用を混在させると破綻しやすい
3. クラス vs ファクトリ関数:選定基準
✅ クラスで設計すべきケース
- “型”の意味が明確(User, CartItem, Timerなど)
- newによるインスタンス制御が自然
-
instanceof
による型判定を活用する場合
✅ ファクトリ関数で構築すべきケース
- 状態のみが必要で、プロトタイプ継承は不要
- データをベースに“加工”する関数が主
- インスタンスを隠蔽し、純粋関数として扱いたい場合
function createUser(id, name) {
return {
id,
name,
isAdmin: false,
displayName() {
return `${this.name}`;
}
};
}
4. 振る舞いを責務単位に分割する
class Cart {
constructor() {
this.items = [];
}
add(item) {
this.items.push(item);
}
total() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
}
- ✅ データ保持(items)と操作(add/total)を意味ある単位で提供
- ✅ クラスが「文脈と機能の集合体」であることを明示する設計
5. 合成 vs 継承:再利用の構造的選択
❌ 継承を多用すると依存関係が崩壊しやすい
class AdminUser extends User {
deleteUser() { ... }
}
→ ✅ 再利用の基本は「合成(Composition)」による柔軟な設計
function withLogger(obj) {
return {
...obj,
log() {
console.log(`[${obj.name}]`);
}
};
}
6. 設計的判断のフレームワーク
目的 | 手法 |
---|---|
状態だけを扱いたい | POJO |
意味ある型と動作を束ねたい | クラス or ファクトリ関数 |
動作を切り替え可能にしたい | Strategyパターン + 合成 |
インスタンス制御したい | クラス構文 + instanceof
|
動作だけ再利用したい | 関数 or Mixin |
よくあるミスと対策
❌ オブジェクトに処理と状態が無秩序に混在
→ ✅ クラスを導入 or 関数を分離し、意味の単位で設計する
❌ 継承で「is-a」関係が破綻(例:AdminUser → GuestUser)
→ ✅ 共通機能は合成(has-a)で再利用
❌ どこからでも直接プロパティを参照してしまう
→ ✅ アクセサを導入し、内部構造を隠蔽
get isLoggedIn() {
return !!this.token;
}
結語
オブジェクトとは、データではなく「概念の実体化」である。
それは構造であり、振る舞いであり、意味を持った単位で再利用可能にする設計の中心である。
- POJOで構造を定義し
- クラスで意味を定義し
- 合成で振る舞いを再利用する
JavaScriptにおけるオブジェクト設計とは、
“意味と再利用を両立させるための、最小構成の建築技法”である。