やった!遂に日本語版がリリースされました!ご意見、ご感想、誤記・誤植などのご報告は、メッセージを送信してください。

ファクトリーの比較

この記事では 以下を比較し 違いを明らかにします

  1. ファクトリー
  2. 生成メソッド
  3. 静的生成 つまりファクトリー メソッド
  4. 単純ファクトリー
  5. Factory Method パターン
  6. Abstract Factory パターン

これらの用語は ウェブ上至る所で使われているのが見つけられます 似たように見えますが それぞれ違う意味を持っています 多くの人々はそのことを知らず 混乱や誤解につながっています

ということで 違いを理解し この問題を一気に片付けてみましょう

1. ファクトリー

ファクトリー 工場 何かを生み出すことになっている関数 メソッド クラスを指す 曖昧な用語です 最も一般的な使われ方として ファクトリーはオブジェクトを生み出します しかしファクトリーは ファイルやデータベースのレコードなども生み出すかもしれません

たとえば くだけた場面では 以下のいずれも ファクトリー と呼ばれるかもしれません

  • プログラムの GUI を作成する関数またはメソッド
  • ユーザーを作成するクラス
  • クラスのコンストラクターをある特定の方法で呼び出す静的メソッド
  • 生成に関するデザインパターンの一つ

通常 誰かが ファクトリー という言葉を使った時 何を意味しているかは 文脈から明らかなはずです でも疑問に思ったら尋ねていてください 単にその人がよくわかっていないだけかもしれません

2. 生成メソッド

生成メソッド cre­ation method パターン指向リファクタリング入門 の中で オブジェクトを生成するメソッド と定義されています この意味するところはつまり Factory Method パターンの結果は 生成メソッドですが 逆は必ずしも真ではない ということです また 生成メソッド の用語は Martin Fowler マーティン・ファウラー 氏が リファクタリング 既存のコードを安全に改善する の中で言う ファクトリー・メソッド という用語 Joshua Bloch ジョシュア・ブロック 氏が Effective Java 邦題同 の中で言う 静的ファクトリー・メソッド という用語と置き換え可能です

実際のところ 生成メソッドはコンストラクター呼び出しのラッパーにすぎません 単に意図をより良く言い表せる名前とも言えます 一方で コンストラクターの変更からコードを隔離する一助になるかもしれません あるいは新規オブジェクトを作成する代わりに 既存のものを返すなどの特定のロジックを含ませることすらできます

新しいオブジェクトを生み出すからという理由だけで 多くの人たちがそのようなメソッドのことを ファクトリー・メソッド と呼びます メソッドはオブジェクトを作成し すべてのはオブジェクトを作成するので これは明らかに という単純な論理に基づいています 当然のことながら 本物の Factory Method パターンのことを話す時に 大混乱となります

下記の例では next 生成メソッドです

class Number {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function next() {
        return new Number ($this->value + 1);
    }
}

3. 静的生成メソッド

静的生成メソッド sta­t­ic cre­ation method sta­t­ic と宣言された生成メソッドです 言い換えると クラスに対して呼び出すことができ 呼び出しに際してオブジェクトを必要としません

この種のメソッドのことを誰かが 静的ファクトリー・メソッド と呼んだからと言って驚かないでください ただの習慣です Factory Method 継承に頼ったデザインパターンです それを sta­t­ic としてしまうと サブクラスではもう拡張できず このパターンの目的に反したものとなります

静的生成メソッドが新規オブジェクトを返すなら コンストラクターの代替となります

以下の場合に便利かもしれません

  • 異なる目的のために異なるコンストラクターがいくつか必要だが コンストラクターのシグネチャーが一致してしまう時 たとえば Ran­dom­(int max)Ran­dom­(int min) の両方を持つことは Java C++ C# そして他の多くの言語では不可能です この問題点の回避方法として一番よく使われる方法は デフォルト・コンストラクターを呼んで その後適切な値を設定する静的メソッドをいくつか作成することです

  • 新規オブジェクトを作成する代わりに 既存のオブジェクトを再利用したい時 [Singleton] パターンをご覧ください ほとんどのプログラミング言語では コンストラクターはクラスの新インスタンスを返さなければなりません 静的作成メソッドは この制限の回避策です 静的メソッド内部では コンストラクターを呼び出して新鮮なインスタンスを作成すべきか キャッシュにある既存オブジェクトを返すべきかを 自分のコードが決めます

下記の例では load メソッドは 静的生成メソッドで ユーザーをデータベースから取得する便利な方法を提供します

class User {
    private $id, $name, $email, $phone;

    public function __construct($id, $name, $email, $phone) {
        $this->id = $id;
        $this->name = $name;
        $this->email = $email;
        $this->phone = $phone;
    }

    public static function load($id) {
        list($id, $name, $email, $phone) = DB::load_data('users', 'id', 'name', 'email', 'phone');
        $user = new User($id, $name, $email, $phone);
        return $user;
    }
}

4. ・パターン

単純ファクトリー sim­ple fac­to­ry パターン  巨大な条件分岐のついた生成メソッドを持つクラスのことです メソッドのパラメーターに基づき どのプロダクトのクラスをインスタンス化すべきかを決め 生成し 返します

人々は通常 を一般的なファクトリーか あるいは生成に関するデザインパターンのうちの一つと勘違いします 大抵の場合 単純ファクトリーは Factory Method パターンか Abstract Factory パターンを導入する時の中間ステップです

単純ファクトリーは 一つのクラス中の一つのメソッドとして表現されます 時間が経過するにつれて このメソッドは大きくなりすぎ メソッドの一部をサブクラスに抽出すべきかどうか検討を始めます そして抽出を何回か繰り返すと 典型的な Factory Method パターンになっていることに気が付くでしょう

ちなみに 単純ファクトリーを abstract と宣言さえすれば 魔法のように Abstract Factory パターンになるわけではありません

単純ファクトリーの例です

class UserFactory {
    public static function create($type) {
        switch ($type) {
            case 'user': return new User();
            case 'customer': return new Customer();
            case 'admin': return new Admin();
            default:
                throw new Exception('Wrong user type passed.');
        }
    }
}

5. Factory Method パターン

Factory Method  生成に関するデザインパターンの一つで 生成するオブジェクトのインターフェースを宣言し サブクラスで作成されるオブジェクトの型を自由に変更できるようにします

もし基底クラスに生成メソッドがあり それをサブクラスで拡張していたら Factory Method が使われているのかもしれません

abstract class Department {
    public abstract function createEmployee($id);

    public function fire($id) {
        $employee = $this->createEmployee($id);
        $employee->paySalary();
        $employee->dismiss();
    }
}

class ITDepartment extends Department {
    public function createEmployee($id) {
        return new Programmer($id);
    }
}

class AccountingDepartment extends Department {
    public function createEmployee($id) {
        return new Accountant($id);
    }
}

6. Abstract Factory パターン

Abstract Factory  生成に関するデザインパターンの一つで 関連あるいは依存関係にあるオブジェクトのファミリーを 具象クラスを指定せずに可能にします

オブジェトのファミリー って何だ ですって たとえば このクラスの集まりを考えてみてください 輸送手段エンジン制御方法 これらには 以下のような 変種があるかもしれません

  1. 燃焼エンジンハンドル
  2. 飛行機ジェットエンジン操縦桿

あなたのプログラムにプロダクトのファミリーのようなものがないのであれば Abstract Factory を使う必要はありません

繰り返しますが 多くの人が Abstract Factory のことを abstract と宣言された単純ファクトリーと勘違いしています 十分注意してください

後書き

違いがわかったところで これらのデザインパターンについて改めて再読してみてください