JavaRush /Java Blog /Random-JA /デザインパターン: FactoryMethod

デザインパターン: FactoryMethod

Random-JA グループに公開済み
こんにちは!今日は引き続きデザインパターンの勉強とファクトリーメソッド(FactoryMethod)についてお話します。 デザインパターン: FactoryMethod - 1このテンプレートが何であるか、そしてこのテンプレートがどのようなタスクに適しているかがわかります。この設計パターンを実際に見て、その構造を探っていきます。上記のすべてを明確にするには、次のトピックを理解する必要があります。
  1. Java の継承。
  2. Java の抽象メソッドとクラス。

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

すべての工場設計パターンには、作成者 (工場自体) と製品 (工場によって作成されたオブジェクト) という 2 つの参加者グループが存在します。状況を想像してみてください。当社には AutoRush ブランドの自動車を生産する工場があります。彼女は、さまざまなタイプのボディを持つ自動車モデルを作成する方法を知っています。
  • セダン
  • ステーションワゴン
  • クーペ
私たちの状況は非常に順調だったので、ある晴れた日、OneAuto の懸念を吸収しました。賢明な経営者として、私たちは OneAuto の顧客を失いたくありません。私たちの任務は、次のようなものを生産できるように生産を再構築することです。
  • オートラッシュセダン
  • オートラッシュ ステーションワゴン
  • クーペ AutoRush
  • ワンオートセダン
  • OneAutoステーションワゴン
  • ワンオートクーペ
ご覧のとおり、派生製品の 1 つのグループではなく、いくつかの詳細が異なる 2 つの派生製品が登場しました。ファクトリ メソッドデザイン パターンは、それぞれに何らかの特性を持つさまざまな製品グループを作成するという問題を解決します。前回の講義で作成したコーヒーショップの例を使用して、このテンプレートの原理を実際に検討し、単純なものから複雑なものへと徐々に移行していきます。

ファクトリーテンプレートについて少し

思い出させてください。私たちはあなたと一緒に小さな仮想コーヒー ショップを構築しました。その中で、私たちは簡単な工場を使ってさまざまな種類のコーヒーを作る方法を学びました。今日はこの例を改良していきます。シンプルな工場を備えたコーヒーショップがどのようなものか思い出してみましょう。コーヒー教室がありました:
public class Coffee {
    public void grindCoffee(){
        // перемалываем кофе
    }
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
そして彼の後継者の何人か、つまり私たちの工場で生産できる特定の種類のコーヒー:
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("Вот ваш кофе! Спасибо, приходите еще!");
        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("Вот ваш кофе! Спасибо, приходите еще!");
    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) {
        /*
            Закажем капучино в итальянском стиле:
            1. Создадим фабрику для приготовления итальянского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику итальянского кофе
            3. Закажем наш кофе
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);


         /*
            Закажем капучино в американском стиле
            1. Создадим фабрику для приготовления американского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику американского кофе
            3. Закажем наш кофе
         */
        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(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
ステップ 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("Вот ваш кофе! Спасибо, приходите еще!");
        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);
    }
}
おめでとう。ファクトリ メソッド設計パターンをコーヒー ショップに実装したところです。

ファクトリメソッドの仕組み

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

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

デザインパターン: FactoryMethod - 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