Interactive

Factory Method Pattern

プロンプトに「Factory」を含めることで、ClaudeCodeはオブジェクト生成ロジックを一箇所に集約し、生成処理の変更や拡張を容易にする実装を生成できます。弊社では、データベースエンティティの生成、APIレスポンスの変換、条件に応じたインスタンス生成で複雑化を避けたい場合にFactory Methodパターンを使用します。

弊社の推奨ルール

  1. 生成ロジックの集約 - オブジェクト作成処理を専用クラスに分離
  2. 型安全性の確保 - 型マップとジェネリクスで戻り値の型を明確化
  3. 条件分岐の削減 - if-elseではなくマップベースの生成処理
  4. イミュータブル設計 - 生成されるオブジェクトの不変性を保証
  5. 拡張性の重視 - 新しい生成パターンを容易に追加可能

ClaudeCodeでの利用

型安全なFactoryの作成

複数タイプのオブジェクト生成を型安全に行いたい場合

「UserFactoryクラスを作成して。admin、member、guestタイプのユーザーを生成できるように」

条件分岐をFactoryに変換

if-else文による生成処理をより保守性の高い設計に変更

「このif-else文をFactoryパターンに置き換えて」

データ変換Factory

APIレスポンスをドメインオブジェクトに変換する処理を実装

「APIレスポンスからモデルオブジェクトを生成するFactoryを実装」

複数のif-elseではなくFactoryパターンを使う

条件分岐によるオブジェクト生成は、Factoryパターンで一箇所に集約します。if-else文の増加による保守性の低下を防ぎ、新しいタイプの追加を容易にします。

// ❌ 避ける:複数のif-else
function createUser(type: string, data: UserData) {
  if (type === 'admin') {
    return new AdminUser(data, ['all']);
  } else if (type === 'member') {
    return new MemberUser(data, ['read']);
  } else if (type === 'guest') {
    return new GuestUser(data, []);
  } else {
    throw new Error('Unknown user type');
  }
}

Factoryパターンではまず型マップとクリエイタ関数を定義します。

// ✅ 推奨:Factoryパターン
type UserTypeMap = {
  admin: AdminUser;
  member: MemberUser;
  guest: GuestUser;
};

class UserFactory {
  private static creators = {
    admin: (data: UserData) => new AdminUser(data, ['all']),
    member: (data: UserData) => new MemberUser(data, ['read']),
    guest: (data: UserData) => new GuestUser(data, []),
  };

型安全な生成メソッドでif-elseを置き換えます。

  static create<T extends keyof UserTypeMap>(
    type: T, 
    data: UserData
  ): UserTypeMap[T] {
    const creator = this.creators[type];
    if (!creator) {
      throw new Error(`Unknown user type: ${type}`);
    }
    return creator(data) as UserTypeMap[T];
  }
}

使用例では型安全にユーザーを生成できます。

// 使用例:型安全にユーザーを生成
const adminUser = UserFactory.create('admin', userData); // AdminUser型
const memberUser = UserFactory.create('member', userData); // MemberUser型

このパターンにより、新しいユーザータイプの追加時はcreatorsオブジェクトに追加するだけで済み、既存のコードを変更する必要がありません。

よくある問題

Factoryパターンでの型推論の欠如

Factoryメソッドの戻り値の型が不明確な問題です。型マップとジェネリクスで型安全性を確保することで解決できます。

// ❌ 型が不明確
function createEntity(type: string) {
  if (type === 'user') return new User();
  if (type === 'product') return new Product();
  return null;
}

型マップとジェネリクスで型安全な生成を実現します。

// ✅ 型安全なFactory
type EntityMap = {
  user: User;
  product: Product;
};

function createEntity<T extends keyof EntityMap>(type: T): EntityMap[T] {
  const factories = {
    user: () => new User(),
    product: () => new Product(),
  };
  return factories[type]() as EntityMap[T];
}

生成ロジックが複雑になる

Factoryメソッド内で複雑な初期化処理を行ってしまう問題です。Builderパターンと組み合わせることで解決できます。

// ❌ 複雑な生成ロジック
class UserFactory {
  static createAdmin(data: UserData) {
    const permissions = ['read', 'write', 'delete'];
    const settings = getDefaultAdminSettings();
    const profile = createAdminProfile(data);
    return new AdminUser(data, permissions, settings, profile);
  }
}

// ✅ Builderパターンとの組み合わせ
class UserFactory {
  static createAdmin(data: UserData): AdminUser {
    return new AdminUserBuilder()
      .withData(data)
      .withAllPermissions()
      .withDefaultSettings()
      .build();
  }
}

Q: 静的メソッドとインスタンスメソッドどちらを使うべき?

A: 弊社では外部依存がない場合は静的メソッド、DIが必要な場合はインスタンスメソッドを使用します。

Q: 抽象ファクトリとの使い分けは?

A: 単一オブジェクトの生成はFactory Method、関連オブジェクト群の生成は抽象ファクトリを使用します。