5、ファクトリーモード
要件の例
ピザ プロジェクトを見てみましょう。ピザの種類を拡張するのは簡単で、メンテナンスも簡単です。
-
ピザの種類も豊富(ギリシャピザ、チーズピザなど)
-
ピザの製造プロセスには、準備、焼き、カット、箱詰めが含まれます。
-
ピッツェリアの注文機能を完了します。
従来のアプローチを使用する (デザインパターンを使用しない)
Pizza クラスとそのサブクラス:
/**
* @author cVzhanshi
* @create 2023-04-17 16:45
*/
@Slf4j
public abstract class Pizza {
public abstract void prepare();
public void bake() {
log.info("bake()");
}
public void cut() {
log.info("cut()");
}
public void box() {
log.info("box()");
}
}
@Slf4j
public class GreekPizza extends Pizza {
@Override
public void prepare() {
log.info("prepare() ---> GreekPizza");
}
}
...
注文クラス
@Slf4j
public class OrderPizza {
public void generateOrder(String type) {
Pizza pizza;
if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("cheese")) {
pizza = new CheesePizza();
} else {
log.info("选择错误");
return;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
分析します:
- コードが理解しやすくなりました
- これはオープンクローズの原則に違反しています。つまり、拡張にはオープンであり、変更にはクローズです。新しいタイプのピザを追加する場合は、新しいクラスを追加してから、注文ビジネスに if ブランチを追加する必要があります。注文ビジネスは複数のストアに展開される場合があります。注文ビジネスはユーザーであり、ユーザーはそれに応じて閉じられる必要があります。つまり、変更は許可されておらず、変更は拡張内でのみ行うことができます。
5.1 単純なファクトリーパターン
-
簡易ファクトリパターン(静的ファクトリパターン)は、作成パターンに属し、ファクトリパターンの一種である。単純なファクトリ パターンは、ファクトリ オブジェクトによって作成される製品クラスのインスタンスを決定することです。シンプル ファクトリ パターンは、ファクトリ パターン ファミリの中で最もシンプルで最も実用的なパターンです。
-
単純なファクトリ パターン: オブジェクトを作成するクラスを定義し、このクラスはオブジェクトをインスタンス化する動作 (コード) をカプセル化します。
-
ソフトウェア開発では、多数のオブジェクトを使用して特定のタイプ、特定のタイプ、または特定のバッチのオブジェクトを作成する場合、ファクトリ パターンを使用します。
単純なファクトリ パターンは上記の例を実装します。
アイデア: Pizza オブジェクトの作成をクラスにカプセル化することで、新しいタイプの Pizza を作成したときにクラスを変更するだけで済み、Pizza オブジェクトを作成する他のコードを変更する必要がなくなります。
ファクトリクラス:
/**
* @author cVzhanshi
* @create 2023-04-17 17:14
*/
public class SimplePizzaFactory {
public static Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("cheese")) {
pizza = new CheesePizza();
}
return pizza;
}
}
注文クラスの変更:
@Slf4j
public class OrderPizza {
public void generateOrder(String type) {
Pizza pizza = SimplePizzaFactory.createPizza(type);
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
}
5.2 ファクトリメソッドパターン
異議申し立ての場合に新しい要件を追加します。
ピザ プロジェクトの新しい要件: ピザを注文する際、顧客は北京のチーズ ピザ、北京のペッパー ピザ、ロンドンのチーズ ピザ、ロンドンのペッパー ピザなど、さまざまな味のピザを注文できます。
新しい要件の場合は、単純なファクトリー モデルを使用することもできますが、プロジェクトの規模やソフトウェアの保守性と拡張性を考慮すると、あまり良いとは言えません。
アイデア: ファクトリ メソッド パターンを使用する
ファクトリ メソッド パターンの概要
オブジェクトを作成するための抽象メソッドが定義されており、どのクラスをインスタンス化するかはサブクラスによって決定されます。ファクトリ メソッド パターンは、オブジェクトのインスタンス化をサブクラスに延期します。
コード例:
-
まずピザクラスを抽象的に作成し、メソッドとしてステップを作成します。
/** * @author cVzhanshi * @create 2023-04-17 16:48 */ @Slf4j public abstract class OrderPizza { public abstract Pizza createPizza(String type); public void generateOrder(String type) { Pizza pizza = createPizza(type); if (pizza != null) { pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } } }
-
さまざまなフレーバーに応じてさまざまな実装クラスを作成する
@Slf4j public class BJOrderPizza extends OrderPizza{ @Override public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("greek")) { pizza = new BJGreekPizza(); } else if (type.equals("cheese")) { pizza = new BJCheesePizza(); } return pizza; } } @Slf4j public class LDOrderPizza extends OrderPizza{ @Override public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("greek")) { pizza = new LDGreekPizza(); } else if (type.equals("cheese")) { pizza = new LDCheesePizza(); } return pizza; } }
5.3 抽象ファクトリーパターン
-
抽象ファクトリ パターン: 特定のクラスを指定せずに、関連または依存するオブジェクト クラスターを作成するためのインターフェイスを定義します。
-
抽象ファクトリ パターンは、単純なファクトリ パターンとファクトリ メソッド パターンを統合できます。
-
ファクトリを、AbsFactory (抽象ファクトリ) と具象ファクトリのサブクラスの 2 つの層に抽象化します。プログラマは、作成するオブジェクトのタイプに応じて、対応するファクトリ サブクラスを使用できます。これにより、単一の単純なファクトリ クラスがファクトリ クラスタに変わり、コードのメンテナンスと拡張がより容易になります。
コード例:
-
抽象ファクトリーパターンの抽象化層(インターフェース)
/** * @author cVzhanshi * @create 2023-04-18 14:53 */ public interface AbsFactory { Pizza createPizza(String orderType); }
-
抽象サブクラス
/** * @author cVzhanshi * @create 2023-04-18 14:55 */ public class LDFactory implements AbsFactory { @Override public Pizza createPizza(String orderType) { Pizza pizza = new BJCheesePizza(); if (orderType.equals("cheese")) { pizza = new LDCheesePizza(); } else if (orderType.equals("greek")) { pizza = new LDGreekPizza(); } return pizza; } } /** * @author cVzhanshi * @create 2023-04-18 14:55 */ public class BJFactory implements AbsFactory { @Override public Pizza createPizza(String orderType) { Pizza pizza = new BJCheesePizza(); if (orderType.equals("cheese")) { pizza = new BJCheesePizza(); } else if (orderType.equals("greek")) { pizza = new BJGreekPizza(); } return pizza; } }
-
オーダークラスは、最下位で使用されるファクトリクラスを無視できます。
/** * @author cVzhanshi * @create 2023-04-18 15:06 */ public class OrderPizza { private AbsFactory absFactory; public OrderPizza(AbsFactory absFactory, String ordertype) { this.absFactory = absFactory; Pizza pizza; pizza = absFactory.createPizza(ordertype); if (pizza != null) { pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } } }
5.4 JDKコードでの反映
Calendar.getInstance(); は次のコードのような良い例です。
public static Calendar getInstance(Locale aLocale)
{
return createCalendar(TimeZone.getDefault(), aLocale);
}
public static Calendar getInstance(TimeZone zone,
Locale aLocale)
{
return createCalendar(zone, aLocale);
}
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
5.5 概要
- ファクトリ パターンの重要性は、インスタンス化されたオブジェクトのコードを抽出し、それを統合管理および保守用のクラスに配置して、メイン プロジェクトの依存関係からの分離を実現することです。これにより、プロジェクトの拡張性と保守性が向上します。
- オブジェクトインスタンスを作成する際は、直接新規クラスを作成するのではなく、その新規クラスのアクションをファクトリメソッドに入れて返します。一部の本では、変数は特定のクラスへの参照を直接保持すべきではないと述べています。
- クラスは具象クラスを継承させるのではなく、抽象クラスを継承するかインターフェース(インターフェース)を実装してください。
- 基本クラスにすでに実装されているメソッドをオーバーライドしないでください。