分岐地獄からの脱出：オブジェクトマップパターンで書く、もっと読みやすいコード（TypeScript, JavaScript）

こんにちは！

コードレビューで何度も指摘しているのが、複雑化したif文やswitch文の問題です。
それを解決する一つの手段が「**オブジェクトマップパターン**」です。
このパターンを使えば、コードの保守性が高まり、他のメンバーにも理解しやすくなります。
ぜひチームに導入してみてください。


## こんなことありませんか？
### if文やswitch文が多用されていてコードを読んでいて仕様が整理できない
→じつはこれ、単純に複雑な実装になっていて他人が見てとても理解しにくいソースコードになっています。拡張性にも乏しいです。例を挙げてみます

```typescript

// 😱 こんなコード見たことありませんか？
function getUserStatus(user: User, context: Context) {
 let status = '';
 
 switch (user.role) {
   case 'admin':
     if (context.isWeekend) {
       if (user.isOnVacation) {
         status = 'away';
       } else {
         status = 'limited';
       }
     } else {
       status = 'active';
     }
     break;
   case 'user':
     if (context.isBusinessHours) {
       switch (user.subscription) {
         case 'premium':
           status = 'active';
           break;
         case 'basic':
           if (user.creditsRemaining > 0) {
             status = 'limited';
           } else {
             status = 'inactive';
           }
           break;
         default:
           status = 'inactive';
       }
     } else {
       status = 'offline';
     }
     break;
   // さらに続く...
 }
 
 return status;
}
```

このコードを一見して仕様を理解できますか？日本語で整理すると以下のようになります：

* **管理者の場合**
  * 週末なら
     * 休暇中なら「away」
     * 休暇中でなければ「limited」
  * 平日なら「active」
* **一般ユーザーの場合**
  * 営業時間内なら
     * プレミアム会員なら「active」
     * 一般会員で
        * クレジット残高があれば「limited」
        * クレジット残高がなければ「inactive」
     * その他のサブスクなら「inactive」
  * 営業時間外なら「offline」
* **その他のロール**
  * ...

こう箇条書きにしてもらうと、「あぁ、なるほど」と思いますよね。でもコードからこれを読み取るのは一苦労です。特に「さらに続く...」部分が実際にあったら、もう大変なことになります。
これを見て「すぐに把握できますか？」と聞かれたら、正直「いいえ」と答えざるを得ません。コードレビューでこんなコードを見ると、理解するだけで時間がかかってしまいます。

## Switch文の何が問題か？
レビューで何度も指摘している問題点を挙げてみます：
### 1. 新しいケースを追加するたびに既存コードを変更する必要がある

```
typescript// 😓 新しいロールを追加するたびに...
switch (userRole) {
  case 'admin':
    // 処理...
    break;
  case 'editor':
    // 処理...
    break;
  // 新しいロールを追加 → 既存のswitch文を変更する必要
  case 'moderator':  // ← ここに追加
    // 処理...
    break;
  default:
    // 処理...
}
```

「機能追加のためにすでに動いているコードを変更する」というのは、バグを生み出す温床です。既存のコードは触らずに新しい機能を追加できるのが理想です。

### 2. テストを書くのが大変（そして書かれない）
巨大なswitch文があるファイルで問題となるのが、テストカバレッジが正確に検出されないことがあることです。
「全パターンをテストするのが大変だから…」という理由でテストが書かれないケースをよく見ます

### 3. 責務の分離ができていない
一つの関数の中で様々なロジックが混ざっていると、修正が必要になったときに影響範囲を把握するのが難しくなります。

## オブジェクトマップパターンで解決しよう
オブジェクトマップパターンは、分岐処理をオブジェクトのプロパティとして定義するアプローチです。
```typescript
// 🎯 各ロールの処理を独立した関数として定義
const userSubscriptionHandlers = {
  premium: () => 'active',
  basic: (user: User) => user.creditsRemaining > 0 ? 'limited' : 'inactive',
  default: () => 'inactive'
};

const statusHandlers = {
  admin: (user: User, context: Context) => {
    if (context.isWeekend) {
      return user.isOnVacation ? 'away' : 'limited';
    }
    return 'active';
  },
  
  user: (user: User, context: Context) => {
    if (!context.isBusinessHours) {
      return 'offline';
    }
    
    const subscriptionHandler = 
      userSubscriptionHandlers[user.subscription] || 
      userSubscriptionHandlers.default;
    
    return subscriptionHandler(user);
  }
};

// デフォルトのハンドラも定義可能
const defaultStatusHandler = (user: User, context: Context) => {
  // デフォルトの処理
  return 'inactive';
};

// 使う時はこれだけ
function getUserStatus(user: User, context: Context) {
  const handler = statusHandlers[user.role] || defaultStatusHandler;
  return handler(user, context);
}
```

## オブジェクトマップパターンのメリット

### 1. 新機能の追加がシンプルになる

```typescript
// 😊 新しいロールを追加したい？オブジェクトに追加するだけ！
statusHandlers.moderator = (user: User, context: Context) => {
 // モデレーター特有のロジック
 return 'moderating';
};
```

### 2. テストがとても書きやすくなる
```typescript
// 😎 個別にテストできるから簡単！
describe('statusHandlers', () => {
  it('admin：週末で休暇中なら away', () => {
    const result = statusHandlers.admin(
      { role: 'admin', isOnVacation: true },
      { isWeekend: true }
    );
    expect(result).toBe('away');
  });
  
  it('user：営業時間外なら offline', () => {
    const result = statusHandlers.user(
      { role: 'user' },
      { isBusinessHours: false }
    );
    expect(result).toBe('offline');
  });
});
```
各ハンドラを個別にテストできるので、テストカバレッジが自然と上がる

### 可読性の著しい向上

### 動的な処理も柔軟に対応できる
```typescript
// 🚀 特別なイベント中だけ異なる処理をしたい場合も簡単
const getStatusHandler = (isSpecialEvent: boolean) => {
  if (isSpecialEvent) {
    return {
      ...statusHandlers,
      // 特別なイベント中は全員アクティブ
      user: () => 'active'
    };
  }
  return statusHandlers;
};

function getUserStatus(user: User, context: Context) {
  const handlers = getStatusHandler(context.isSpecialEvent);
  const handler = handlers[user.role] || defaultStatusHandler;
  return handler(user, context);
}
```

### 伝えたかった事
大きなSwitch文を書こうとしたとき、あるいはレビューで見かけたときは、「これ、オブジェクトマップにできないかな？」と考えてみてください。

そうすることで未来の自分（とチームメイト）からの「これ複雑すぎ…」というコメントを防げるはずです。

わたしは予約システムを開発しているのですが、基幹機能である空き状況の検索機能の仕様がなかなかに複雑でifやswitch文が入り組んでいて開発者が正確に仕様を把握しきれない問題があったのをオブジェクトマッピングパターンを用いて解決しました。
結果**可読性が上がりレビューもやりやすくなり、カバレッジ検出も正確になったため、リリース後の不具合もかなり少なくなりました。**(半分どころじゃないくらい)

皆さんも是非、使ってみてくださいね。

※***Switch文が適している場面もあります***
→ 補足すると、もちろんすべてのswitch文がダメなわけではありません。シンプルな場合は問題ありません