2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおけるクラス vs 関数ベースの設計戦略:状態・継承・柔軟性のトレードオフ分析

Posted at

概要

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における構造設計とは、
“構文ではなく責務と変化耐性を設計することで、進化に耐えるコードを作る戦略である。”

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?