ファクトリパターン

オブジェクトの生成ロジックをカプセル化し、生成方法の詳細を隠蔽するデザインパターン

設計パターンオブジェクト指向

ファクトリパターンとは

ファクトリパターンは、オブジェクトの生成ロジックをカプセル化し、呼び出し側から生成方法の詳細を隠蔽する設計の総称である。GoF の生成パターンとして収録されているのは Factory Method と Abstract Factory の 2 つで、実務で「ファクトリ」と呼ばれるものの多くは、この考え方を関数 1 つに縮めた形を指している。

ファクトリの種類

ファクトリの種類を以下にまとめる。

種類説明
Simple Factory条件分岐でオブジェクトを生成
Factory Method生成の窓口だけを定め、どのクラスを実体化するかはサブクラスが決める
Abstract Factory関連するオブジェクト群を、具体クラス名を出さずにまとめて生成

このうち Simple Factory は GoF の 23 パターンには含まれない通称で、静的ファクトリメソッドや生成関数とも呼ばれる。設計の議論では「ファクトリで」と言われた側がサブクラス階層を前提の Factory Method を想像していることがあるため、どれを指しているかを先に揃えておくと手戻りが減る。

Simple Factory

Simple Factory のコード例を示す。

type NotificationType = 'email' | 'sms' | 'slack';

interface Notification {
  send(message: string): Promise<void>;
}

function createNotification(type: NotificationType): Notification {
  switch (type) {
    case 'email': return new EmailNotification();
    case 'sms': return new SmsNotification();
    case 'slack': return new SlackNotification();
  }
}

// 呼び出し側は具体的なクラスを知らない
const notification = createNotification('email');
await notification.send('Hello');

この形の弱点は switch にある。通知手段が増えるたびに関数本体を書き換えるので、追加に対して閉じていない。分岐が伸びてきたら、種類をキーに生成関数を引く表 (レジストリ) へ置き換えるか、生成の判断そのものをサブクラス側へ移す Factory Method に寄せる。

AWS サービスのファクトリ

AWS サービスのファクトリのコード例を示す。

// 環境に応じて異なるクライアントを生成
function createDbClient() {
  const endpoint = process.env.DYNAMODB_ENDPOINT;
  return endpoint
    ? new DynamoDBClient({ endpoint }) // ローカル開発
    : new DynamoDBClient({});          // AWS 環境
}

ファクトリ関数 vs クラス

ファクトリ関数と クラス のコード例を比較する。

// ファクトリ関数 (公開するのが振る舞いだけのとき)
function createUser(name: string, role: string) {
  return {
    name,
    role,
    createdAt: new Date(),
    isAdmin: () => role === 'admin',
  };
}

// クラス (継承や instanceof での判別を使うとき)
class User {
  constructor(public name: string, public role: string) {}
  isAdmin() { return this.role === 'admin'; }
}

公開したいのが振る舞いだけなら、ファクトリ関数の方が短く済み、戻り値をインターフェースで縛れるので呼び出し側が内部構造に依存しにくい。ただしこの書き方はオブジェクトを作るたびにメソッドの関数オブジェクトを作り直し、instanceof による判別もできない。大量に作る値オブジェクトや、型で分岐したい階層ではクラスの方が扱いやすい。

いつファクトリを使うか

生成を関数に切り出す価値は、呼び出し側に「どのクラスを new するか」を知らせずに済ませたいかで決まる。

場面判断理由
生成前に検証や既定値の補完などの手順がある使う手順を 1 箇所に集められ、生成後の状態が必ず整っていることを呼び出し側で確かめ直さずに済む
条件によって返す実装を切り替えたい使う上の createNotification のように分岐をファクトリ内に閉じ込め、呼び出し側は共通のインターフェースだけを見て書ける
テストで本物の依存を差し替えたい使う生成箇所が 1 つなので、テストではその関数だけを別実装に向ければよく、利用側のコードには手を入れない
フィールドを詰めるだけのオブジェクト使わない直接生成と比べて分かることが増えず、追う先が 1 段深くなるだけになる

ファクトリパターンの背景や設計思想は関連書籍に詳しい。

この記事は役に立ちましたか?

関連用語

関連する記事