工廠設計模式(Factory Design Pattern)是一種建立型的設計模式,它提供了一種建立物件的最佳方式,是一種代替 new
操作符的一種模式。
在工廠模式中,建立物件不會對使用者端暴露建立邏輯,而是通過使用一個共同的介面來指向新建立的物件。
工廠模式還可以細分為三種的型別:簡單工廠模式、工廠方法模式和抽象工廠模式。
簡單工廠模式(Simple Factory Pattern)的定義是,由一個工廠物件決定建立出哪一種產品類的範例,被建立的產品類範例具有共同的父類別或實現同樣的介面。
因為在簡單工廠模式中用於建立範例的方法通常是靜態方法,因此簡單工廠模式又被稱為靜態工廠模式(Static Factory Pattern)。
下面是使用果汁生產來展示簡單工廠模式:
果汁 FruitJuice 介面:描述生產果汁的必要方法
public interface FruitJuice {
void make();
}
蘋果汁 AppleJuice 類:生產蘋果汁的類
public class AppleJuice implements FruitJuice {
public AppleJuice() {
this.make();
}
@Override
public void make() {
System.out.println("This is AppleJuice make!");
}
}
橙汁 OrangeJuice 類:生產橙汁的類
public class OrangeJuice implements FruitJuice {
public OrangeJuice() {
this.make();
}
@Override
public void make() {
System.out.println("This is OrangeJuice make!");
}
}
果汁工廠 FruitJuiceFactory 類:負責生產各種果汁的類
public class FruitJuiceFactory {
public FruitJuiceFactory() {}
public static FruitJuice createFruitJuice(String juiceType) {
FruitJuice fruitJuice = null;
if (juiceType.equals("AppleJuice")) {
fruitJuice = new AppleJuice();
} else if (juiceType.equals("OrangeJuice")) {
fruitJuice = new OrangeJuice();
}
return fruitJuice;
}
}
通過 if-else 邏輯是簡單工廠的第一種實現方法,還可以通過靜態程式碼塊和 Map 結構實現簡單工廠模式,具體的程式碼範例如下:
import java.util.Map;
import java.util.HashMap;
public class FruitJuiceFactory {
// 儲存產品類的對映關係
private static final Map<String, FruitJuice> cachedFruitJuice = new HashMap<>();
static {
cachedFruitJuice.put("AppleJuice", new AppleJuice());
cachedFruitJuice.put("OrangeJuice", new OrangeJuice());
}
public FruitJuiceFactory() {}
public static FruitJuice createFruitJuice(String juiceType) {
if (juiceType == null || juiceType.isEmpty()) {
return null;
}
// 直接從 Map 結構中取到對應的產品類範例
return cachedFruitJuice.get(juiceType.toLowerCase());
}
}
簡單工廠模式的主要優點如下:
簡單工廠模式的主要缺點如下:
簡單工廠模式的適用場景如下:
在 JDK 中,java.util.Calendar
使用了簡單工廠模式。如下是其的一些實現邏輯:
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
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) {
cal = switch (caltype) {
case "buddhist" -> new BuddhistCalendar(zone, aLocale);
case "japanese" -> new JapaneseImperialCalendar(zone, aLocale);
case "gregory" -> new GregorianCalendar(zone, aLocale);
default -> null;
};
}
}
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;
}
}
簡單工廠模式違背了開閉原則,沒有能夠做到「對擴充套件開放、對修改關閉」,新增產品類時需要改動到工廠類。
而工廠方法模式(Factory Method Pattern)是對簡單工廠模式的進一步抽象,其好處是可以使系統在不修改原來程式碼的情況下引入新的產品類,即滿足開閉原則。
和簡單工廠模式中工廠負責生產所有的產品相比,工廠方法模式抽象出簡單工廠的介面,然後將生產具體產品的任務分發給具體的產品工廠,即簡單工廠的工廠。
由於工廠方法模式利用了物件導向的多型特性,因此又被稱為多型工廠模式(Polymorphic Factory Pattern)。
在上述簡單工廠模式的基礎上,將果汁生產改造成工廠方法模式:
抽象工廠 AbstractFactory 介面:描述生產果汁工廠的必要方法
public interface FruitJuiceFactory {
FruitJuice createFruitJuice();
}
蘋果汁工廠 AppleJuiceFactory 類:詳細的生產蘋果汁的類
public class AppleJuiceFactory implements FruitJuiceFactory {
public AppleJuiceFactory() {}
@Override
public FruitJuice createFruitJuice() {
return new AppleJuice();
}
}
橙汁工廠 OrangeJuiceFactory 類:詳細的生產橙汁的類
public class OrangeJuiceFactory implements FruitJuiceFactory {
public OrangeJuiceFactory() {}
@Override
public FruitJuice createFruitJuice() {
return new OrangeJuice();
}
}
工廠方法模式除了具有簡單工廠模式的優點之外,還有以下優點:
工廠方法模式的主要缺點如下:
工廠方法模式的適用場景如下:
在 JDK 中,java.util.Collection
介面中定義的 iterator()
方法就是一個工廠方法。
所有實現了 Collection
介面的類,都需要顯式地實現此方法並返回一個 Iterator
範例,不同的實現類可以擁有自己的實現邏輯。
在工廠方法模式中,具體工廠負責生產具體的產品,每個具體工廠對應一種具體產品,工廠方法具有唯一性。
抽象工廠模式(Abstract Factory Pattern)定義了一個介面用於建立相關或有依賴關係的物件族,而無需指明具體的類,其可以整合簡單工廠模式和抽象工廠模式。
學習抽象工廠模式需要理解兩個概念:產品等級結構指的是產品的繼承結構,如抽象果汁和具體果汁之間構成一個產品等級結構;產品族指的是由同一個工廠生產的,位於不同等級結構中的一組產品,如同一個飲料工廠生產的酒和果汁構成一個產品族。
從上述概念上理解,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式在工廠方法模式的基礎上,覆蓋了多個工廠的產品族。
在上述簡單工廠模式的基礎上,下面使用果汁生產和酒生產來展示抽象工廠模式:
酒 Alcohol 介面:描述生產酒的必要方法
public interface Alcohol {
void make();
}
葡萄酒 Wine 類:詳細的生產葡萄酒的類
public class Wine implements Alcohol {
public Wine() {
this.make();
}
@Override
public void make() {
System.out.println("This is Wine make!");
}
}
啤酒 Beer 類:詳細的生產啤酒的類
public class Beer implements Alcohol {
public Beer() {
this.make();
}
@Override
public void make() {
System.out.println("This is Beer make!");
}
}
抽象工廠 AbstractFactory 介面:描述生產果汁和酒的必要方法
public interface AbstractFactory {
FruitJuice creatFruitJuice();
Alcohol createAlcohol();
}
具體工廠 ConcreteFactory 類:可生產果汁和酒的工廠的類
public class ConcreteFactory implements AbstractFactory {
public ConcreteFactory() {}
@Override
public FruitJuice creatFruitJuice(String juiceType) {
FruitJuice fruitJuice = null;
if (juiceType.equals("AppleJuice")) {
fruitJuice = new AppleJuice();
} else if (juiceType.equals("OrangeJuice")) {
fruitJuice = new OrangeJuice();
}
return fruitJuice;
}
@Override
public Alcohol createAlcohol(String alcoholType) {
Alcohol alcohol = null;
if (alcoholType.equals("Wine")) {
alcohol = new Wine();
} else if (alcoholType.equals("Beer")) {
alcohol = new Beer();
}
return alcohol;
}
}
抽象工廠模式的主要優點如下:
抽象工廠模式的主要缺點如下:
抽象工廠模式的適用場景如下:
在 JDK 中,java.util.Collection
介面可以看作一個抽象工廠。
Collection
介面定義了轉換成 Iterator
範例的工廠方法,也定義了轉換成 T[]
範例的工廠方法,可以認定其做了兩個產品族的定義。
對於要不要使用工廠模式,其最本質的參考標準有以下四個:
對於產品種類相對較少、且可預見性地不會修改的情況,可以使用簡單工廠模式。使用簡單工廠模式的使用者端只需傳入工廠類的引數,不需要關心如何建立物件的邏輯,可以很方便地建立所需產品。
由於簡單工廠模式會將所有建立邏輯都放在一個工廠類中,會導致這個工廠類會變得很複雜,當產品的種類是可預見地會增加時,還需要對工廠類做更改,這種時候可以採用工廠方法模式以達到不需要修改原來程式碼的情況下引進新的產品。
抽象工廠模式最早的應用是用於建立屬於不同作業系統的視窗構件。當需要建立的物件是一系列相互關聯或相互依賴的產品族時,或者是隻會新增新的產品族而不是新種類的產品時,可以使用抽象工廠模式。