こんにちは!今日は引き続きデザインパターンの勉強とファクトリーメソッド(FactoryMethod)についてお話します。 このテンプレートが何であるか、そしてこのテンプレートがどのようなタスクに適しているかがわかります。この設計パターンを実際に見て、その構造を探っていきます。上記のすべてを明確にするには、次のトピックを理解する必要があります。
- Java の継承。
- Java の抽象メソッドとクラス。
ファクトリメソッドはどのような問題を解決しますか?
すべての工場設計パターンには、作成者 (工場自体) と製品 (工場によって作成されたオブジェクト) という 2 つの参加者グループが存在します。状況を想像してみてください。当社には AutoRush ブランドの自動車を生産する工場があります。彼女は、さまざまなタイプのボディを持つ自動車モデルを作成する方法を知っています。- セダン
- ステーションワゴン
- クーペ
- オートラッシュセダン
- オートラッシュ ステーションワゴン
- クーペ AutoRush
- ワンオートセダン
- OneAutoステーションワゴン
- ワンオートクーペ
ファクトリーテンプレートについて少し
思い出させてください。私たちはあなたと一緒に小さな仮想コーヒー ショップを構築しました。その中で、私たちは簡単な工場を使ってさまざまな種類のコーヒーを作る方法を学びました。今日はこの例を改良していきます。シンプルな工場を備えたコーヒーショップがどのようなものか思い出してみましょう。コーヒー教室がありました: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 つのポイントを開きます。変更は内装だけでなくドリンクにも影響します。- イタリアのコーヒーショップでは、特別な粉砕と焙煎を施したイタリアのコーヒーブランドのみを使用します。
- アメリカの部分は少し大きくなり、注文ごとにとろけるマシュマロ、つまりマシュマロを提供します。
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
う。答えは「はい、できます」です。これをファクトリメソッドデザインパターンと呼びます。
シンプルファクトリーからファクトリーメソッドへ
問題をできるだけ効率的に解決するために、次のことを行います。- メソッドを
createCoffee(CoffeeType type)
クラスに返しましょうCoffeeShop
。 - このメソッドを抽象化しましょう。
- クラス自体が
CoffeeShop
抽象化されます。 - クラスには
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);
}
}
おめでとう。ファクトリ メソッド設計パターンをコーヒー ショップに実装したところです。
ファクトリメソッドの仕組み
では、得られたものを詳しく見てみましょう。以下の図は、結果として得られるクラスを示しています。緑色のブロックはクリエーター クラス、青色のブロックはプロダクト クラスです。 どのような結論が導き出せるでしょうか?- すべての製品は抽象クラスの実装です
Coffee
。 - すべての作成者は抽象クラスの実装です
CoffeeShop
。 - 2 つの並列クラス階層が観察されます。
- 製品の階層。イタリア人の子孫とアメリカ人の子孫がいます
- クリエイターの階層。イタリア人の子孫とアメリカ人の子孫がいます
- スーパークラスには、
CoffeeShop
どの特定の製品実装 (Coffee
) が作成されるかについての情報はありません。 - スーパークラスは、
CoffeeShop
特定の製品の作成をその子孫に委任します。 - 各子孫クラスは、その詳細に従って
CoffeeShop
ファクトリ メソッドを実装します。createCoffee()
言い換えれば、クリエーター クラスの実装内で、クリエーター クラスの詳細に基づいて特定の製品を準備することが決定されます。
ファクトリメソッドの構造
上の図は、ファクトリ メソッド パターンの一般的な構造を示しています。ここで他に重要なことは何ですか?- Creator クラスには、ファクトリ メソッドを除く、製品と対話するすべてのメソッドの実装が含まれています。
- 抽象メソッドは、
factoryMethod()
クラスのすべての子孫によって実装される必要がありますCreator
。 - このクラスは、プロダクトを直接生成する
ConcreteCreator
メソッドを実装します。factoryMethod()
- このクラスは、特定の製品の作成を担当します。これは、これらの製品の作成に関する情報が含まれる唯一のクラスです。
- すべての製品は共通のインターフェイスを実装する必要があります。つまり、共通の製品クラスの子孫である必要があります。これは、プロダクトを使用するクラスが、具体的な実装ではなく抽象化のレベルでプロダクトを操作できるようにするために必要です。
GO TO FULL VERSION