1定義
工廠模式(Factory Pattern)屬於創建型設計模式。如其名它提供了一種像實際場景中像加工廠的形式來進行創建類對象,其過程不會對客戶端暴露創建邏輯,並且是通過使用共同的接口來返回新創建的對象。一般工廠模式雙可分爲簡單工廠模式(Simple Factory)、工廠方法模式(Factory Method)和 抽象工廠模式(Abstract Factory),其中嚴格來講簡單工廠不算是一個標準的的設計模式,但是工廠方法和抽象工廠都是通過簡單工廠演變而來。
2 簡單工廠模式
在介紹簡單工廠模式前,我們先來構思一個現實生活中真實的場景。假設有一個手機廠商要生產智能手機和智能手錶兩款硬件產品,我們用代碼來表達一般會這樣寫:
public class Phone {
public void showInfo() {
System.out.println("手機性能很強悍!");
}
}
public class Watch {
public void showInfo() {
System.out.println("手錶時尚時尚最時尚!");
}
}
// 客戶端調用
public class Main {
public static void main(String[] args){
Phone phone = new Phone();
phone.showInfo();
Watch watch = new Watch();
watch.showInfo();
}
}
代碼就是對現實的映照,所以上述代碼是沒有錯的,但是這樣的思維卻使程序只爲滿足當前的需求,程序不容易維護、擴展和複用。例如要生產多個產品就需要new出多次,這種暴露的創建對象是完全可以將其封裝起來。又如手機廠商應該專心做好手機的設計和系統調優,生產這種事情就交給代工廠來完成就好了。
2.1 普通簡單工廠
那麼我們來看看使用簡單工廠模式來表達是怎麼樣的。
手機和手錶都屬於硬件,將它們抽象出來:
public abstract class Hardware {
public abstract void showInfo();
}
public class Phone extends Hardware {
@Override
public void showInfo() {
System.out.println("手機性能很強悍!");
}
}
public class Watch extends Hardware {
@Override
public void showInfo() {
System.out.println("手錶時尚時尚最時尚!");
}
}
硬件工廠類:
public class HardwareFactory {
public static Hardware createHardware(String hardwareName) {
Hardware hardware = null;
switch (hardwareName) {
case "phone":
hardware = new Phone();
break;
case "watch":
hardware = new Watch();
break;
default:
throw new UnsupportedOperationException("不支持該操作");
}
return hardware;
}
}
調用者:
public class Main {
public static void main(String[] args){
Hardware phone = HardwareFactory.createHardware("phone");
phone.showInfo();
Hardware watch = HardwareFactory.createHardware("watch");
watch.showInfo();
}
}
上述代碼中,因爲手機和手錶都是屬於硬件類產品,所以將它們繼承於一個硬件類,客戶端也就是手機廠商只需要給硬件工廠一個明確的命令,說我要生產一個手機或手錶的硬件,調用硬件工廠中的createHardware方法傳入要生成的硬件名,硬件工廠收到命令就會按要求生產出對應的硬件產品。
2.2 多方法簡單工廠
普通的簡單工廠有一個明顯的缺點,就是隻有生產硬件的一個對外方法,需要接收一個約定好的命名來進行生產對應的產品,這樣如果客戶端傳遞命令名稱錯誤,則不能正確地創建出產品對象,而這時可以將工廠類進行一個改進,明確出能生產什麼樣的產品。代碼如:
public class HardwareFactory {
public static Hardware createPhone() {
return new Phone();
}
public static Hardware createWatch() {
return new Watch();
}
}
調用者:
public class Main {
public static void main(String[] args){
Hardware phone = HardwareFactory.createPhone();
phone.showInfo();
Hardware watch = HardwareFactory.createWatch();
watch.showInfo();
}
}
2.3 小結
簡單工廠其實就是將同類產品進行歸類,將對象生產過程進行封裝,這很符合現實中的情況。而且客戶端僅負責消息產品,而免除了直接創建產品對象的責任,這樣就明確了各自的職責和權利,在業務場景較爲簡單時,這也很好地體現出代碼設計的優點。
3 工廠方法模式
如上所述,簡單工廠模式適用於業務場景較爲簡單情況下或者具體產品很少有增加的情況。而對象複雜的業務具體產品在不斷增加的情況下就顯得不太合適。
爲什麼?想象一下上面實際場景,當手機廠商後面開始進軍智能家居如智能燈泡、智能電視等進行生產時,那代碼層面上對產品部分來說是很簡單,只需要創建一個燈泡或電視的類,然後繼承於硬件抽象類便可以了,但是對硬件工廠就不友好了,因爲每增加一個硬件產品,硬件工廠就得進行一次修改,這樣爲了一個新的業務就很容易影響到原有穩定的業務。
由於硬件工廠類中集中了所有產品實例的創建,而我們在開發中並未能預先考慮到後面新的業務場景,工廠承受的責任太重而且經常的改動,很難避免模塊功能的蔓延,對系統的維護和擴展非常不利,這顯然不是一個很好的做法,也就是違背了開閉原則。
遇上如上情景時,就應該由工廠方法模式來解決了。工廠方式模式也叫 “多態工廠模式”。工廠的多態也就是把工廠改造成像產品類一樣進行抽象,由每一個子工廠來負責對應產品的創建。
手機和手錶繼承於硬件抽象類,跟簡單工廠一樣不需要做改變:
public abstract class Hardware {
public abstract void showInfo();
}
public class Phone extends Hardware {
@Override
public void showInfo() {
System.out.println("手機性能很強悍!");
}
}
public class Watch extends Hardware {
@Override
public void showInfo() {
System.out.println("手錶時尚時尚最時尚!");
}
}
將工廠抽象成接口(抽象類也行):
public interface IHardwareFactory {
Hardware createHardware();
}
// 手機子工廠
public class PhoneFactory implements IHardwareFactory {
@Override
public Hardware createHardware() {
return new Phone();
}
}
// 手錶子工廠
public class WatchFactory implements IHardwareFactory {
@Override
public Hardware createHardware() {
return new Watch();
}
}
調用者:
public class Main {
public static void main(String[] args){
IHardwareFactory phoneFactory = new PhoneFactory();
Hardware phone = phoneFactory.createHardware();
phone.showInfo();
IHardwareFactory watchFactory = new WatchFactory();
Hardware watch = watchFactory.createHardware();
watch.showInfo();
}
}
3.1 小結
工廠方法模式是簡單工廠模式的進一步改進版,簡單工廠模式將產品進行了抽象由單個工廠創建和輸出對象,而工廠方法模式還將工廠進一步的抽象,由多個子工廠來創建和輸出對應的產品對象。核心工廠類不再負責所有產品的創建細節,它僅負責給具體子工廠定義必須實現的接口。這使得工廠方法模式可以允許系統在不修改工廠的情況下新增新產品的擴展。工廠方法模式在設計上完全完全符合開閉原則。
當然,工廠方法模式也有其明顯的缺點,就是每增加一個產品時,都需要增加一個具體對應的子工廠,這使得系統中代碼量的增加,在一定程序上也增加了系統的複雜度,這也並不是好事。
4 抽象工廠模式
介紹抽象工廠模式前,我們先來理解兩個概念。
產品等級結構:等級結構又叫繼承結構,所以產品等級結構就是產品的繼承結構。正如上述手機示例中,手機是一個抽象類,而其子類有MZ手機、XM手機、HW手機等。抽象手機與具體品牌的手機之間構成了一個產品等級結構,抽象手機是父類,而具體品牌的手機是其子類。手錶同理。
產品族:位於不同產品等級結構中,有相關聯的產品組成的家族。又正如上述示例中, MZ廠商生產了手機和手錶,還有其他廠商XM、HW等也生產了手機和手錶。MZ手機位於手機產品等級結構中,MZ手錶位於手錶產品等級結構中。而MZ手機和MZ手錶構成了MZ產品族,同理,XM手機和XM手錶構成了XM產品族,HW手機和HW手錶構成了HW產品族。
簡單工廠模式引入了產品等級結構,其繼承關係是硬件和手機、手錶,而工廠方法模式在其基礎上再引入了對應的工廠等級結構來解決了簡單工廠模式中工廠類職責太重的問題,而當產品非常多時就會出現大量的與之對應的工廠類。一旦業務邏輯變得複雜出現一個或多個產品族時,若繼續使用工廠方式將會出現不可想象多的工廠類,而抽象工廠模式恰好就是解決產品等級結構與產品族存在的工廠模式進化版本。
硬件類還是最頂層的抽象類:
public abstract class Hardware {
public abstract void showInfo();
}
// 將手機也抽象出來
public abstract class Phone extends Hardware {
public abstract void slogan();
}
// 將手錶也抽象出來
public abstract class Watch extends Hardware {
public abstract void slogan();
}
產品族的實際產品:
// MZ產品族
public class MzPhone extends Phone {
@Override
public void showInfo() { System.out.println("Mz手機性能很強悍!"); }
@Override
public void slogan() { System.out.println("Mz手機如你才華橫溢!"); }
}
public class MzWatch extends Watch {
@Override
public void showInfo() { System.out.println("Mz手錶時尚時尚最時尚!"); }
@Override
public void slogan() { System.out.println("Mz手錶如你才華橫溢!"); }
}
// XM產品族
public class XmPhone extends Phone {
@Override
public void showInfo() { System.out.println("Xm手機性能很強悍!"); }
@Override
public void slogan() { System.out.println("Xm手機年輕人第一臺手機!"); }
}
public class XmWatch extends Watch {
@Override
public void showInfo() { System.out.println("Xm手錶時尚時尚最時尚!"); }
@Override
public void slogan() { System.out.println("Xm手機年輕人第一隻手錶!"); }
}
// HW產品族
public class HwPhone extends Phone {
@Override
public void showInfo() { System.out.println("Hw手機性能很強悍!"); }
@Override
public void slogan() { System.out.println("Hw手機自主芯片就是牛!"); }
}
public class HwWatch extends Watch {
@Override
public void showInfo() { System.out.println("Hw手錶時尚時尚最時尚!"); }
@Override
public void slogan() { System.out.println("Hw手錶自主芯片就是牛!"); }
}
工廠:
// 類似多方法簡單工廠模式,但將其抽象
public interface IHardwareFactory {
Hardware createPhone();
Hardware createWatch();
}
// MZ產品族工廠,就是一個多方法簡單工廠
public class MzHardwareFactory implements IHardwareFactory {
@Override
public Phone createPhone() {
return new MzPhone();
}
@Override
public Watch createWatch() {
return new MzWatch();
}
}
// XM產品族工廠,就是一個多方法簡單工廠
public class XmHardwareFactory implements IHardwareFactory {
@Override
public Phone createPhone() {
return new XmPhone();
}
@Override
public Watch createWatch() {
return new XmWatch();
}
}
// HW產品族工廠,就是一個多方法簡單工廠
public class HwHardwareFactory implements IHardwareFactory {
@Override
public Phone createPhone() {
return new HwPhone();
}
@Override
public Watch createWatch() {
return new HwWatch();
}
}
調用者:
public class Main {
public static void main(String[] args){
MzHardwareFactory mzHardwareFactory = new MzHardwareFactory();
Phone mzPhone = mzHardwareFactory.createPhone();
mzPhone.showInfo();
mzPhone.slogan();
Watch mzWatch = mzHardwareFactory.createWatch();
mzWatch.showInfo();
mzWatch.slogan();
XmHardwareFactory xmHardwareFactory = new XmHardwareFactory();
Phone xmPhone = xmHardwareFactory.createPhone();
xmPhone.showInfo();
xmPhone.slogan();
Watch xmWatch = xmHardwareFactory.createWatch();
xmWatch.showInfo();
xmWatch.slogan();
HwHardwareFactory hwHardwareFactory = new HwHardwareFactory();
Phone hwPhone = hwHardwareFactory.createPhone();
hwPhone.showInfo();
hwPhone.slogan();
Watch hwWatch = hwHardwareFactory.createWatch();
hwWatch.showInfo();
hwWatch.slogan();
}
}
輸出結果:
Mz手機性能很強悍!
Mz手機如你才華橫溢!
Mz手錶時尚時尚最時尚!
Mz手錶如你才華橫溢!
Xm手機性能很強悍!
Xm手機年輕人第一臺手機!
Xm手錶時尚時尚最時尚!
Xm手機年輕人第一隻手錶!
Hw手機性能很強悍!
Hw手機自主芯片就是牛!
Hw手錶時尚時尚最時尚!
Hw手錶自主芯片就是牛!
4.1 小結
抽象工廠模式中,由每個工廠來統一管理一個產品族,單個工廠內生產不同的產品等級結構的產品。其實更直白地說,抽象工廠就是當存在產品族關係時,將簡單工廠模式和工廠方法模式相結合,形成以產品族作爲區分的多套簡單工廠模式。抽象工廠模式在新增產品族時是符合開閉原則,工廠方法模式是在新增具體產品時增加對應產品的工廠,而抽象工廠模式是在新增一個產品族時才需要新增工廠。
但是抽象工廠模式在新增產品等級結構時就顯然需要修改所有的產品族工廠,例如示例中要新增一個智能電視產品時,就要對三個產品族工廠進行修改,從而顯示很糟糕。所以我們作爲程序的開發者應該儘可能地在設計之初就要多考慮未來場景,否則將會導致系統出現較多的修改從而增加維護的成本。