# 機能設計書 5-EventDispatcher

## 概要

本ドキュメントは、Three.jsライブラリにおけるイベント駆動システムを提供するEventDispatcherクラスの機能設計について記述する。EventDispatcherは、カスタムJavaScriptオブジェクトに対してイベントの発行・購読機能を追加するための基底クラスとして機能する。

### 本機能の処理概要

EventDispatcherクラスは、イベント駆動プログラミングパターンを実装するための軽量なイベントシステムを提供する。オブジェクトにイベントリスナーの登録・削除・発行機能を付与し、疎結合なコンポーネント間通信を実現する。

**業務上の目的・背景**：3Dアプリケーションでは、様々なコンポーネント（オブジェクト、ジオメトリ、マテリアル、レンダラーなど）が相互に連携して動作する必要がある。EventDispatcherは、これらのコンポーネント間の依存関係を最小化しながら、状態変化やアクションの通知を効率的に行うための仕組みを提供する。これにより、コードの保守性と拡張性が向上する。

**機能の利用シーン**：
- Object3Dの追加・削除時のイベント通知（added、removed、childadded、childremoved）
- BufferGeometryやRenderTargetのリソース解放通知（dispose）
- アニメーションの再生状態変化通知
- コントローラーの状態変化通知（Controls系）
- カスタムアプリケーションイベントの実装

**主要な処理内容**：
1. **リスナー登録**: addEventListener()によるイベントリスナーの登録
2. **リスナー確認**: hasEventListener()による登録確認
3. **リスナー削除**: removeEventListener()によるイベントリスナーの削除
4. **イベント発行**: dispatchEvent()によるイベントオブジェクトの発行

**関連システム・外部連携**：
- Object3D、BufferGeometry、RenderTarget等のThree.jsクラスが継承
- アプリケーションコードでのカスタムイベント実装

**権限による制御**：特になし（ライブラリレベルの機能のため、アプリケーション側での権限管理は行わない）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 40 | ユニットテスト | 主機能 | EventDispatcherクラスのテスト |

## 機能種別

イベント管理 / コンポーネント通信

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | string | Yes | イベントタイプ名 | addEventListener/removeEventListener/dispatchEvent |
| listener | Function | Yes | コールバック関数 | addEventListener/removeEventListener |
| event | Object | Yes | イベントオブジェクト | dispatchEvent、typeプロパティ必須 |

### 入力データソース

- アプリケーションコードからの直接呼び出し
- Three.js内部からの自動呼び出し（Object3D.add()等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| hasEventListener戻り値 | boolean | リスナーが登録されているか |
| event.target | Object | イベント発行元オブジェクト（dispatchEvent時に自動設定） |

### 出力先

- 登録されたイベントリスナー（コールバック関数）への通知
- hasEventListener()の戻り値

## 処理フロー

### 処理シーケンス

```
1. リスナー登録
   └─ addEventListener(type, listener)
       └─ _listenersが未定義なら初期化
       └─ type用の配列が未定義なら初期化
       └─ 重複チェック（既存なら追加しない）
       └─ 配列にlistenerを追加

2. リスナー確認
   └─ hasEventListener(type, listener)
       └─ _listenersが未定義ならfalse
       └─ type用配列にlistenerが含まれるかチェック

3. リスナー削除
   └─ removeEventListener(type, listener)
       └─ _listenersが未定義ならreturn
       └─ type用配列からlistenerを検索
       └─ 見つかった場合はsplice()で削除

4. イベント発行
   └─ dispatchEvent(event)
       └─ _listenersが未定義ならreturn
       └─ event.typeに対応するリスナー配列を取得
       └─ event.targetにthisを設定
       └─ 配列をコピー（反復中の削除対策）
       └─ 各リスナーをcall()で実行
       └─ event.targetをnullにリセット
```

### フローチャート

```mermaid
flowchart TD
    A[EventDispatcher] --> B{操作種別}
    B -->|登録| C[addEventListener]
    B -->|確認| D[hasEventListener]
    B -->|削除| E[removeEventListener]
    B -->|発行| F[dispatchEvent]

    C --> C1{_listeners存在?}
    C1 -->|No| C2[_listeners = {}]
    C1 -->|Yes| C3{type配列存在?}
    C2 --> C3
    C3 -->|No| C4[listeners[type] = []]
    C3 -->|Yes| C5{重複チェック}
    C4 --> C5
    C5 -->|既存| C6[何もしない]
    C5 -->|新規| C7[配列にpush]

    D --> D1{_listeners存在?}
    D1 -->|No| D2[return false]
    D1 -->|Yes| D3{type配列にlistener含む?}
    D3 -->|No| D2
    D3 -->|Yes| D4[return true]

    E --> E1{_listeners存在?}
    E1 -->|No| E2[return]
    E1 -->|Yes| E3{type配列存在?}
    E3 -->|No| E2
    E3 -->|Yes| E4{listenerのindex検索}
    E4 -->|見つからない| E2
    E4 -->|見つかった| E5[splice削除]

    F --> F1{_listeners存在?}
    F1 -->|No| F2[return]
    F1 -->|Yes| F3{event.type配列存在?}
    F3 -->|No| F2
    F3 -->|Yes| F4[event.target = this]
    F4 --> F5[配列をコピー]
    F5 --> F6[各listenerをcall]
    F6 --> F7[event.target = null]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 重複登録防止 | 同一type/listenerの組み合わせは1度しか登録できない | addEventListener()実行時 |
| BR-02 | target自動設定 | dispatchEvent()時にevent.targetが自動設定される | dispatchEvent()実行時 |
| BR-03 | 反復中削除対策 | dispatchEvent()時は配列をコピーしてから反復 | dispatchEvent()実行時 |
| BR-04 | target自動リセット | dispatchEvent()完了後にevent.targetはnullにリセット | dispatchEvent()完了時 |
| BR-05 | 遅延初期化 | _listenersオブジェクトは初回addEventListener()時に作成 | addEventListener()初回実行時 |

### 計算ロジック

**リスナー重複チェック:**
```javascript
if (listeners[type].indexOf(listener) === -1) {
    listeners[type].push(listener);
}
```

**リスナー削除:**
```javascript
const index = listenerArray.indexOf(listener);
if (index !== -1) {
    listenerArray.splice(index, 1);
}
```

## データベース操作仕様

### 操作別データベース影響一覧

該当なし（EventDispatcherはデータベースを使用しない）

## エラー処理

### エラーケース一覧

該当なし（EventDispatcherは明示的なエラー処理を行わない）

### リトライ仕様

該当なし

## トランザクション仕様

該当なし（メモリ上のオブジェクト操作のみ）

## パフォーマンス要件

- リスナーの登録・削除はO(n)の計算量（nは登録リスナー数）
- イベント発行はO(n)の計算量（nは該当タイプのリスナー数）
- 大量のリスナー登録時はメモリ使用量に注意
- 頻繁なイベント発行がある場合は配列コピーのオーバーヘッドを考慮

## セキュリティ考慮事項

該当なし（クライアントサイドのグラフィックスライブラリ）

## 備考

- EventDispatcherはThree.js専用のシンプルな実装であり、DOM EventsやNode.js EventEmitterとは異なる
- メインリポジトリは [eventdispatcher.js](https://github.com/mrdoob/eventdispatcher.js/)
- イベントオブジェクトには任意のプロパティを追加可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: クラス構造を理解する

EventDispatcherの基本構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | EventDispatcher.js | `src/core/EventDispatcher.js` | クラス全体の構造 |

**読解のコツ**: EventDispatcherはプロパティを持たず、メソッドのみで構成される。`_listeners`は遅延初期化されるプライベートプロパティ。

#### Step 2: リスナー登録を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | EventDispatcher.js | `src/core/EventDispatcher.js` | addEventListener() |

**主要処理フロー**:
- **31-49行目**: addEventListener()
- **33行目**: `_listeners`の遅延初期化
- **37-41行目**: type用配列の遅延初期化
- **43-47行目**: 重複チェックとpush

#### Step 3: リスナー確認を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | EventDispatcher.js | `src/core/EventDispatcher.js` | hasEventListener() |

**主要処理フロー**:
- **58-66行目**: hasEventListener()
- **62行目**: `_listeners`未定義チェック
- **64行目**: 配列内の存在確認

#### Step 4: リスナー削除を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | EventDispatcher.js | `src/core/EventDispatcher.js` | removeEventListener() |

**主要処理フロー**:
- **74-94行目**: removeEventListener()
- **78行目**: `_listeners`未定義チェック
- **80-92行目**: 配列からの削除（splice使用）

#### Step 5: イベント発行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | EventDispatcher.js | `src/core/EventDispatcher.js` | dispatchEvent() |

**主要処理フロー**:
- **101-126行目**: dispatchEvent()
- **105行目**: `_listeners`未定義チェック
- **107行目**: type用配列の取得
- **111行目**: event.targetの設定
- **114行目**: 配列のコピー（反復中削除対策）
- **116-120行目**: 各リスナーの呼び出し
- **122行目**: event.targetのリセット

### プログラム呼び出し階層図

```
EventDispatcher
    │
    ├─ addEventListener(type, listener)
    │      ├─ if (_listeners === undefined) _listeners = {}
    │      ├─ if (listeners[type] === undefined) listeners[type] = []
    │      └─ if (indexOf(listener) === -1) push(listener)
    │
    ├─ hasEventListener(type, listener)
    │      ├─ if (_listeners === undefined) return false
    │      └─ return indexOf(listener) !== -1
    │
    ├─ removeEventListener(type, listener)
    │      ├─ if (_listeners === undefined) return
    │      ├─ index = indexOf(listener)
    │      └─ if (index !== -1) splice(index, 1)
    │
    └─ dispatchEvent(event)
           ├─ if (_listeners === undefined) return
           ├─ listenerArray = listeners[event.type]
           ├─ if (listenerArray === undefined) return
           ├─ event.target = this
           ├─ array = listenerArray.slice(0)
           ├─ for (listener of array) listener.call(this, event)
           └─ event.target = null
```

### データフロー図

```
[入力]                      [処理]                        [出力]

type, listener ─────→ addEventListener() ───┐
                                             │
                                             ↓
                                      ┌─────────────┐
                                      │ _listeners  │
                                      │ {          │
                                      │   type1: [l1, l2],
                                      │   type2: [l3],
                                      │   ...      │
                                      │ }          │
                                      └─────────────┘
                                             │
type, listener ─────→ hasEventListener() ───┤──→ boolean
                                             │
type, listener ─────→ removeEventListener()─┤
                                             │
event {type, ...} ──→ dispatchEvent() ──────┘
                            │
                            ↓
                     ┌─────────────┐
                     │ event.target│
                     │   = this    │
                     └─────────────┘
                            │
                            ↓
                     ┌─────────────┐
                     │ listener1() │
                     │ listener2() │
                     │ ...         │
                     └─────────────┘
                            │
                            ↓
                     ┌─────────────┐
                     │ event.target│
                     │   = null    │
                     └─────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| EventDispatcher.js | `src/core/EventDispatcher.js` | ソース | EventDispatcherクラス本体 |
| Object3D.js | `src/core/Object3D.js` | ソース | 継承先（added, removed, childadded, childremoved） |
| BufferGeometry.js | `src/core/BufferGeometry.js` | ソース | 継承先（dispose） |
| RenderTarget.js | `src/core/RenderTarget.js` | ソース | 継承先（dispose） |
| AnimationMixer.js | `src/animation/AnimationMixer.js` | ソース | 継承先（loop, finished） |
