LoginSignup
1

More than 3 years have passed since last update.

posted at

updated at

Abstract Factory

Abstract Factoryパターンとは

Abstract Factoryパターンは、Factory of Factoriesと呼ばれており、クライアントが抽象クラスを使って、具体的な製品を知ることなく、製品を作成することができます。

Abstract Factoryパターンの使い方

Factoryパターンで示した例をもとに考えていきます。
Factoryパターンでは、FactoryクラスがGyudonクラスを継承したオブジェクトを生成し、返却していました。この場合、Gyudonクラスで使用する食材(BeefやRiceなど)は各クラス毎に設定することができます。つまり、店舗毎に使う食材を決めることができてしまいます。管理する側としては、店舗毎に食材の品質が変わることは好ましくないので、どうにか対処しなくてはなりません。そこでAbstractFactoryパターンを使います。AbstractFactoryパターンでは、使う一連の食材を定義しておき、その定義されたクラスを使えば、製品ができてしまうというものです。管理する側も定義されている食材しか使われないため、管理が容易になります。

実装を見ていきましょう。
日本の店舗であるJapaneseGyudonStoreクラスは、日本の店舗で使用する食材一覧が定義されているJapaneseGyudonIngredientFactoryクラスを使います。返却するGyudonオブジェクト生成時にこのFactoryオブジェクトを渡すことで、Gyudonが使う食材を渡すことができます。

JapaneseGyudonStore.java
public class JapaneseGyudonStore extends GyudonStore {

    public Gyudon createGyudon(GyudonTypeEnum type) {

        Gyudon gyudon = null;
        GyudonIngredientFactory factory = new JapaneseGyudonIngredientFactory();

        if (type.equals(GyudonTypeEnum.NORMAL)) {
            gyudon = new NormalGyudon(factory);
            gyudon.setName("Japanese Normal Gyudon");

        } else if (type.equals(GyudonTypeEnum.CHEESE)) {
            gyudon = new CheeseGyudon(factory);
            gyudon.setName("Japanese Cheese Gyudon");

        } else if (type.equals(GyudonTypeEnum.KIMCHI)) {
            gyudon = new KimchiGyudon(factory);
            gyudon.setName("Japanese Kimchi Gyudon");
        }

        return gyudon;
    }
}

続いて、Gyudonクラスを継承したNormalGyudonクラスです。
NormalGyudonクラスは、GyudonIngredientFactoryインタフェースをフィールドで保有して使用しています。このことをコンポジションと言います。コンポジションとは、別クラスのオブジェクトを自分のクラスに取り込んで使うことを指します。
NormalGyudonクラスはコンストラクタ生成時に渡されたGyudonIngredientFactoryインタフェースを保持して、それをprepare()メソッドで使用しています。

NormalGyudon.java
public class NormalGyudon extends Gyudon{

    GyudonIngredientFactory factory;

    public NormalGyudon(GyudonIngredientFactory factory) {
        this.factory = factory;
    }

    @Override
    public void prepare() {
        super.beef = factory.getBeef();
        super.rice = factory.getRice();
        super.cheese = factory.getCheese(UseOrNotEnum.NOT_USE);
        super.kimchi = factory.getKimchi(UseOrNotEnum.NOT_USE);
    }
}

JapaneseGyudonIngredientFactoryクラスは、GyudonIngredientFactoryインタフェースを実装しており、各食材の具象クラスを返却しています。このFactoryクラスは日本の店舗(JapaneseGyudonStoreクラス)からのみ使用されます。

JapaneseGyudonIngredientFactory.java
public class JapaneseGyudonIngredientFactory implements GyudonIngredientFactory {

    @Override
    public Rice getRice() {
        return new JapaneseRice();
    }

    @Override
    public Beef getBeef() {
        return new JapaneseBeef();
    }

    @Override
    public Cheese getCheese(UseOrNotEnum enum1) {
        if (UseOrNotEnum.USE.equals(enum1)) {
            return new JapaneseCheese();
        } else {
            return new NoCheese();
        }
    }

    @Override
    public Kimchi getKimchi(UseOrNotEnum enum2) {
        if (UseOrNotEnum.USE.equals(enum2)) {
            return new JapaneseKimchi();
        } else {
            return new NoKimchi();
        }
    }
}

実行結果は以下の通りです。

GyudonAbstractFactoryTest.java
public class GyudonAbstractFactoryTest {

    public static void main(String[] args) {

        // buy at Japanese shop
        GyudonStore japan = new JapaneseGyudonStore();
        japan.order(GyudonTypeEnum.NORMAL);
        japan.order(GyudonTypeEnum.CHEESE);
        japan.order(GyudonTypeEnum.KIMCHI);

        // buy at Korean shop
        GyudonStore korea = new KoreanGyudonStore();
        korea.order(GyudonTypeEnum.NORMAL);
        korea.order(GyudonTypeEnum.CHEESE);
        korea.order(GyudonTypeEnum.KIMCHI);
    }
}
実行結果.md
cooked:Japanese Normal Gyudon
ingredient
beef: JapaneseBeef
rice: JapaneseRice
topping: No Cheese No Kimchi
++++++++++++++++++++
cooked:Japanese Cheese Gyudon
ingredient
beef: JapaneseBeef
rice: JapaneseRice
topping: JapaneseCheese No Kimchi
++++++++++++++++++++
cooked:Japanese Kimchi Gyudon
ingredient
beef: JapaneseBeef
rice: JapaneseRice
topping: No Cheese JapaneseKimchi
++++++++++++++++++++
cooked:Korean Normal Gyudon
ingredient
beef: KoreanBeef
rice: KoreanRice
topping: No Cheese No Kimchi
++++++++++++++++++++
cooked:Korean Cheese Gyudon
ingredient
beef: KoreanBeef
rice: KoreanRice
topping: KoreanCheese No Kimchi
++++++++++++++++++++
cooked:Korean Kimchi Gyudon
ingredient
beef: KoreanBeef
rice: KoreanRice
topping: No Cheese KoreanKimchi
++++++++++++++++++++

ソースコードはこちら

UML

AbstractFactoryPattern.png

Abstractパターンのメリット・デメリット

メリット

  • 具象クラスに依存せず、抽象クラスやインタフェースに依存した設計
  • 拡張が容易

デメリット

  • クラス間の関係性が複雑で、規模が大きくなればなるほど可読性が低くなる

重要な設計原則

  • 変化する部分をカプセル化する
  • 継承よりコンポジションを好む
  • 実装に対してではなく、インタフェースに対してプログラミングする
  • 相互にやり取りするオブジェクト間には、疎結合設計を使用する
  • クラスは、拡張に対しては開かれた状態であるべきだが、変更に対しては閉じた状態であるべきである
  • 抽象に依存する。具象に依存してはならない

JDKによる実装例

  • javax.xml.parsers.DocumentBuilderFactoryのnewInstance()メソッド
  • javax.xml.transform.TransformerFactoryのnewInstance()メソッド
  • javax.xml.xpath.XPathFactoryのnewInstance()メソッド

Reference

  • Head First デザインパターン〜頭とからだで覚えるデザインパターンの基本
  • techscore

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1