CodeGym /Java Blog /ランダム /デザインパターン:ファクトリーメソッド
John Squirrels
レベル 41
San Francisco

デザインパターン:ファクトリーメソッド

ランダム グループに公開済み
やあ!今日は引き続きデザイン パターンの学習を進め、ファクトリー メソッド パターンについて説明します。 デザインパターン:ファクトリーメソッド - 1 それが何なのか、そしてこのパターンがどのようなタスクに適しているのかがわかります。このデザインパターンを実際に考察し、その構造を検討していきます。すべてを明確にするには、次のトピックを理解する必要があります。
  1. Java の継承。
  2. Java の抽象メソッドとクラス

ファクトリメソッドはどのような問題を解決しますか?

すべての工場設計パターンには、作成者 (工場自体) と製品 (工場によって作成されるオブジェクト) という 2 種類の参加者がいます。次の状況を想像してください。CodeGym ブランドの車を生産する工場があります。さまざまなタイプのボディを持つ車のモデルを作成する方法を知っています。
  • セダン
  • ステーションワゴン
  • クーペ
私たちのビジネスは非常に繁栄し、ある晴れた日、別の自動車メーカーである OneAuto を買収しました。良識ある経営者として、私たちは OneAuto の顧客を失いたくありません。そのため、次のものを生産できるように生産を再構築するという課題に直面しています。
  • コードジムセダン
  • コードジムのステーションワゴン
  • コードジム クーペ
  • ワンオートセダン
  • OneAutoステーションワゴン
  • ワンオート クーペ
ご覧のとおり、1 つの製品グループではなく 2 つの製品グループがあり、特定の詳細が異なります。ファクトリメソッドデザイン パターンは、それぞれがいくつかの特定の特性を持つ、さまざまな製品グループを作成する必要がある場合に使用します。前のレッスンの 1 つで作成したコーヒー ショップの例を使用して、単純なものから複雑なものに徐々に移行しながら、このパターンの指導原理を実際に検討していきます。

ファクトリーパターンについて少し

以前に小さな仮想コーヒー ショップを構築したことを思い出してください。簡単な工場の助けを借りて、私たちはさまざまな種類のコーヒーの作り方を学びました。今日はこの例を作り直します。シンプルな工場を備えたコーヒーショップの様子を思い出してみましょう。コーヒー教室がありました:

public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
そして、私たちの工場で生産できる特定の種類のコーヒーに対応するいくつかの子クラスがあります。

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
注文を簡単にするために列挙型を作成しました。

public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
コーヒー工場自体はこんな感じでした。

public class SimpleCoffeeFactory {
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }
        
        return coffee;
    }
}
そして最終的に、コーヒーショップ自体は次のようになりました。

public class CoffeeShop {

    private final SimpleCoffeeFactory coffeeFactory;

    public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = coffeeFactory.createCoffee(type);
        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }
}

シンプルな工場を近代化する

私たちのコーヒーショップはとても順調に経営しています。非常に多いので、拡張を検討しています。新しい場所をいくつかオープンしたいと考えています。私たちは大胆で進取的なので、退屈なコーヒーショップを作り変えるつもりはありません。それぞれのお店に特別な工夫をしてもらいたいと思っています。したがって、まずはイタリア店とアメリカ店の 2 店舗をオープンします。これらの変更はインテリアデザインだけでなく、提供されるドリンクにも影響を与えます。
  • イタリアのコーヒーショップでは、イタリアのブランドコーヒーのみを使用し、特別な挽き方と焙煎を行います。
  • アメリカの店舗ではより多くの量があり、注文ごとにマシュマロを提供します。
唯一変わらないのは、優れていることが証明されている当社のビジネスモデルです。コード的には、次のことが起こります。製品に対応する 4 つのクラスがありました。

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
しかし、今では 8 つになります:

public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
現在のビジネスモデルを維持したいので、orderCoffee(CoffeeType type)手法の変更はできるだけ少なくしたいと考えています。見てみましょう:

public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Here's your coffee! Thanks! Come again!");
    return coffee;
}
どのような選択肢があるでしょうか? そうですね、ファクトリーの書き方はすでに知っていますよね? すぐに思い浮かぶ最も簡単な方法は、2 つの同様のファクトリーを作成し、目的の実装をコーヒーショップのコンストラクターに渡すことです。これでは喫茶店の格は変わりません。まず、新しいファクトリ クラスを作成し、単純なファクトリを継承させて、createCoffee(CoffeeType type)メソッドをオーバーライドする必要があります。イタリア式コーヒーとアメリカン式コーヒーを作成する工場を書いてみましょう。

public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
これで、必要なファクトリ実装を CoffeeShop に渡すことができます。さまざまなコーヒー ショップにコーヒーを注文するコードがどのようになるかを見てみましょう。たとえば、イタリア風カプチーノとアメリカ風カプチーノ:

public class Main {
    public static void main(String[] args) {
        /*
            Order an Italian-style cappuccino:
            1. Create a factory for making Italian coffee
            2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
        
        
         /*
            Order an American-style cappuccino
            1. Create a factory for making American coffee
            2. Create a new coffee shop, passing the American coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
希望する工場をそれぞれに渡して、2 つの異なるコーヒー ショップを作成しました。一方で、私たちは目的を達成しましたが、その一方で... どういうわけか、これは起業家にとってうまくいきません... 何が問題なのか考えてみましょう。まず工場の多さ。何?さて、新しい場所ごとに独自の工場を作成し、それに加えて、コーヒーショップを作成するときに関連する工場がコンストラクターに確実に渡されるようにする必要がありますか? 第二に、それはまだ単純な工場です。少しだけ近代化されました。しかし、私たちは新しいパターンを学ぶためにここにいます。第三に、別のアプローチは可能ではないでしょうか? コーヒーの準備に関するすべての問題をCoffeeShopさまざまなスタイルのコーヒーを作るための十分な柔軟性を同時に維持しながら、コーヒーの作成と注文へのサービスのプロセスをリンクすることにより、クラスを実現します。答えは「はい、できます」です。これをファクトリメソッドデザインパターンと呼びます。

シンプルファクトリーからファクトリーメソッドへ

タスクをできるだけ効率的に解決するには:
  1. createCoffee(CoffeeType type)メソッドをクラスに返しますCoffeeShop
  2. このメソッドを抽象化します。
  3. クラスCoffeeShop自体が抽象化されます。
  4. クラスCoffeeShopには子クラスがあります。
はい友よ。イタリアのコーヒーショップは、イタリアのバリスタの最高の伝統に従ってメソッドをCoffeeShop実行するクラスの子孫にすぎません。createCoffee(CoffeeType type)さあ、一歩ずつ。ステップ 1.Coffeeクラスを抽象化します。当社には、異なる製品の 2 つのファミリーがあります。それでも、イタリアとアメリカのコーヒーには共通の祖先、つまりCoffeeクラスがあります。抽象化するのが適切でしょう:

public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
ステップ 2.CoffeeShop抽象createCoffee(CoffeeType type)メソッド を使用して抽象を作成する

public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
ステップ 3. 抽象コーヒー ショップの子孫であるイタリアン コーヒー ショップを作成します。createCoffee(CoffeeType type)イタリアのレシピの詳細を考慮して、そのメソッド を実装します。

public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
ステップ 4. アメリカンスタイルのコーヒーショップでも同じことを行います

public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
ステップ 5. アメリカとイタリアのラテがどのように見えるかを確認してください:

public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
おめでとう。例としてコーヒーショップを使用してファクトリーメソッド設計パターンを実装しました。

ファクトリーメソッドの背後にある原理

ここで、何が得られたのかをさらに詳しく考えてみましょう。以下の図は、結果として得られるクラスを示しています。緑色のブロックはクリエーター クラス、青色のブロックはプロダクト クラスです。 デザインパターン:ファクトリーメソッド - 2どのような結論が出せるでしょうか?
  1. すべての製品は抽象Coffeeクラスの実装です。
  2. すべての作成者は抽象CoffeeShopクラスの実装です。
  3. 2 つの並列クラス階層が表示されます。
    • 製品の階層。イタリア人の子孫とアメリカ人の子孫がいます
    • クリエイターの階層。イタリア人の子孫とアメリカ人の子孫がいます
  4. スーパーCoffeeShopクラスには、どの特定の製品 ( Coffee) が作成されるかについての情報はありません。
  5. スーパーCoffeeShopクラスは、特定の製品の作成をその子孫に委任します。
  6. クラスの各子孫は、それぞれの固有の機能に従ってファクトリ メソッドCoffeeShopを実装します。createCoffee()言い換えれば、プロデューサ クラスの実装は、プロデューサ クラスの詳細に基づいて特定の製品を準備します。
これで、ファクトリ メソッドパターンを定義する準備が整いました。ファクトリメソッドパターンはオブジェクトを作成するためのインターフェイスを定義しますが、サブクラスは作成されたオブジェクトのクラスを選択できます。したがって、ファクトリ メソッドはインスタンスの作成をサブクラスに委任します。一般に、定義を覚えておくことは、すべてがどのように機能するかを理解することほど重要ではありません。

ファクトリメソッドの構造

デザインパターン:ファクトリーメソッド - 3上の図は、ファクトリ メソッド パターンの一般的な構造を示しています。ここで他に重要なことは何ですか?
  1. Creator クラスは、ファクトリ メソッドを除く、製品と対話するすべてのメソッドを実装します。
  2. 抽象factoryMethod()メソッドは、クラスのすべての子孫によって実装される必要がありますCreator
  3. このクラスは、プロダクトを直接作成するメソッドConcreteCreatorを実装します。factoryMethod()
  4. このクラスは、特定の製品の作成を担当します。これは、これらの製品の作成に関する情報が含まれる唯一のクラスです。
  5. すべての製品は共通のインターフェイスを実装する必要があります。つまり、製品は共通の製品クラスの子孫である必要があります。これは、プロダクトを使用するクラスが、特定の実装ではなく抽象化としてプロダクトを操作できるようにするために必要です。

宿題

今日はかなり多くの作業を行い、ファクトリーメソッドのデザインパターンを研究しました。マテリアルを強化する時が来ました!演習 1. 別のコーヒー ショップを開くための作業を行います。英国式かスペイン式のコーヒーショップかもしれません。あるいは宇宙船スタイルでも。コーヒーに食品着色料を加えて光らせると、あなたのコーヒーはこの世のものとは思えないものになります。演習 2.前回のレッスンでは、仮想寿司バーまたは仮想ピッツェリアを作成する演習を行いました。さて、あなたの練習は立ち止まらないことです。今日は、ファクトリー メソッド パターンを有利に活用する方法を学びました。この知識を活用して自分のビジネスを拡大する時が来ました ;)
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION