デザインパターン入門(Factory Method)


この記事ではFactory Methodについてまとめます。
wikipediaによると「他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高める」とあります。
参考:Factory Methodパターン

主な登場人物

NO 名前 役割
1 作成者(Creator) 製品をインスタンス化するためのインタフェースを定義する
2 作成者の具象クラス 製品をインスタンス化する。
3 製品(Product) 製品(デザインパターンで作成したいオブジェクト)のインタフェースを定義する
4 製品の具象クラス 製品の実装クラス

このパターンはクラスをインスタンス化する際のインタフェースを上位の抽象クラスで定義しておき、インスタンス化は抽象クラスを実装するサブクラスで行います。このパターンでは「どのクラスをインスタンス化するかは、サブクラスに決定させる」と表現される場合があります。これは作成者の具象クラスは製品に関する知識を持たず(必要な情報は作成者から継承する)、単に利用するサブクラスを決めれば、自動的に利用する製品(製品の具象クラス)が決まるということです。

パターンを実装する
地域別のピザ屋をクラスで表現します。ピザのデータを保持するクラスと、店舗毎のクラスを作成します。

製品

Pizza.java
import java.time.LocalDateTime;
public class Pizza {
    String name;
    String dough;
    String sauce;

    public void accept() {
        System.out.println(LocalDateTime.now() + " お客様から" + this.name + "の注文を承りました");
    };
    public void prepare() {
        System.out.println("準備中・・・");
    };
    public void bake() {
        System.out.println("焼きます");
    };
    public void cut() {
        System.out.println("切ります");
    };
    public void box() {
        System.out.println("詰めます");
    }
    public String getName() {
        return name;
    }
}

製品の具象クラス

TokyoCheesePizza.java
public class TokyoCheesePizza extends Pizza{
    public TokyoCheesePizza() {
        name = "東京で作られたチーズを使ったピザ";
        dough = "東京生地";
        sauce = "東京ソース";
    }
}
OsakaCheesePizza.java
public class OsakaCheesePizza extends Pizza {
    public OsakaCheesePizza() {
        name = "大阪で作られたチーズを使ったピザ";
        dough = "大阪生地";
        sauce = "大阪ソース";
    }
    public void box() {
        System.out.println("大阪の店舗では特別箱でお届けします");
    }
}

作成者

Order.java
public abstract class Order {
    public Pizza OrderPizza(String type) {
        Pizza pz = createPizza(type);
        pz.accept();
        pz.prepare();
        pz.bake();
        pz.cut();
        pz.box();
        return pz;
    }
    abstract Pizza createPizza(String type);
}

作成者の具象クラス

TokyoOrder.java
public class TokyoOrder extends Order {
    public Pizza createPizza(String type) {
        if (type.equals("チーズ")) return new TokyoCheesePizza();
        else return new Pizza();
    }
}
OsakaOrder.java
public class OsakaOrder extends Order {
    public Pizza createPizza(String type) {
        if (type.equals("チーズ")) return new OsakaCheesePizza();
        else return new Pizza();
    }
}

メインクラス

Main.java
public class Main {
    public static void main (String args[]) {
        Order tk = new TokyoOrder();
        Order os = new OsakaOrder();    
        Pizza tkpz = tk.OrderPizza("チーズ");
        System.out.println(tkpz.getName() + "を注文しました");
        Pizza ospz = os.OrderPizza("チーズ");
        System.out.println(ospz.getName() + "を注文しました");
    }
}
結果
2017-05-22T17:02:41.553 お客様から東京で作られたチーズを使ったピザの注文を承りました
準備中・・・
焼きます
切ります
詰めます
東京で作られたチーズを使ったピザを注文しました
2017-05-22T17:02:41.553 お客様から大阪で作られたチーズを使ったピザの注文を承りました
準備中・・・
焼きます
切ります
大阪の店舗では特別箱でお届けします
大阪で作られたチーズを使ったピザを注文しました

この例ではピザ自体を製品としてパターンを適用しました。ピザ屋の注文を作成者に、ピザを製品に置き換えています。ピザの種類が増えていくと製品のクラス数が増加し、作成者の分岐が増えていき、変更の影響をサブクラス(○○Order.java、○○Pizza.java)に絞り込んでいます。