設計模式——工廠方法(在不同的工廠子類中建立單一子類)
工廠方法(Factory Method)
簡單工廠、工廠方法、抽象工廠的區別
簡單工廠使用舉例
如果說我們有一個父類“交通工具”,它有兩個子類“汽車”和“自行車”,我們現在只需要一個工廠負責根據不同的條件建立不同的子類並返回,比如如果條件為“雨天”則建立汽車,條件為“晴天”則建立自行車,這個工廠就是簡單工廠,通過呼叫它的 create 方法,根據方法的引數,建立某一種父類的子類,並將物件返回,create 的呼叫方會用父類介面接收 create 返回的具體子類。
簡單工廠好處
(1)客戶端不需要掌握所有子類型別,無需編寫建立具體子類的邏輯程式碼,只用傳入引數,工廠就建立相應子類返回給客戶端,客戶端用父類引用呼叫子類的不同方法。
(2)當子類的建立邏輯發生修改、子類型別新增時,由於客戶端與子類建立邏輯解耦,因此不需要修改客戶端端程式碼(當有多個客戶端時,減少大量的程式碼修改工作,只需修改一個工廠類中的程式碼即可)
工廠方法
工廠方法和簡單工廠的使用場景是一樣的,都是用於為客戶端建立單一子類物件,只不過簡單工廠只有一個工廠類,工廠類中有一個 create 方法,其中利用邏輯判斷需要為客戶端建立哪種子類(客戶端呼叫:Factory f=new Factory(),f.create())。而工廠方法有一個抽象工廠介面(定義一個 factoryMethod),不同的工廠子類負責建立不同的子類(具體的子類建立邏輯由不同工廠負責撰寫),客戶端呼叫:Factory f=new Factory1(),f.create())
抽象工廠使用舉例
如果說我們有一個父類“汽車”,他有兩個子類“奧迪”和“BYD”,而“奧迪”又包含物件成員“輪子”、“車燈”,賓士也包含物件成員“輪子”、“車燈”,我們最終的目的是建立某種汽車的某個物件引數,比如我現在要得到一種車燈,並輸出它的亮度,我輸入的條件是“晚上”,我們現在需要工廠依據我們輸入的條件“晚上”,建立奧迪的車燈(顯然奧迪的車燈更亮嘛。。。),也就是說,客戶端首先得到工廠的子類奧迪工廠(不論是奧迪工廠還是 BYD 工廠,他們都包含建立具體車燈的createLamp、建立具體輪子的createWheel),然後呼叫奧迪工廠的 createLamp 方法獲取車燈物件AodiLamp,然後呼叫AodiLamp 的 getBrightness 方法輸出亮度。等等!這裡好像有一個問題呀,我們既然說“工廠向用戶遮蔽物件建立的邏輯以及具體物件的型別”,那麼客戶現在應該是不知道車燈的具體型別的呀,而是應該用 Lamp 父類去接收createLamp 返回的具體子類。所以,我們不僅需要(1)汽車抽象工廠(定義具體工廠需要實現的介面:createLamp、createWheel)、(2)具體工廠(繼承抽象工廠,實現createLamp、createWheel介面——建立相應汽車的具體物件)、(3)抽象產品(Lamp、Wheel,客戶端利用該抽象型別指向工廠創建出的具體型別,客戶端不需要知道具體的產品型別是什麼)、(4)具體產品(AodiLamp、BYDLamp、AodiWheel、BYDWheel)
抽象工廠好處
(1)當子類又包含多種物件成員,並且不同子類的物件成員具有相同的父類(AodiLamp、BYDLamp的父類 Lamp),當客戶端向建立某種子類的某物件成員時,客戶端首先獲取具體工廠類(無需知道具體工廠型別,利用抽象工廠引用指向具體工廠),然後呼叫其建立某種物件成員的方法(客戶端不需要知道物件成員的具體型別,客戶端只利用 Lamp 抽象型別作為引用即可,由工廠的建立物件方法返回具體是 AodiLamp 還是 BYDLamp)
(2)適用於建立某系列(版本)中的某種產品,即當前有系列1、系列2、後續還可能建立序列3等等,系列1包含物件成員(A1、B1、C1),系列2包含物件成員(A2、B2、C2),A1和 A2具有相同的父類。客戶端需求為獲取某系列的 A ,然後呼叫 A 的相應方法時,利用工廠方法不僅能夠遮蔽物件建立的邏輯處理細節,而且當客戶端需要建立某一系列的多種物件成員時,可以僅修改具體工廠型別,而改變後續所有的物件成員。
//抽象工廠 interface Factory { A createA(){}; B createB(){}; } //具體工廠1 class Factory1 implements Factory{ A createA(){ return new A1(); } B createB(){ return new B(); } } //具體工廠2 class Factory2 implements Factory{ A createA(){ return new A2(); } B createB(){ return new B2(); } } //抽象產品 class A{ say(){ } } //具體產品1 class A1{ say(){ system.out.println("我是 A1"); } } //具體產品2 class A2{ say(){ system.out.println("我是 A2"); } } class B{ say(){ } } class B1{ say(){ system.out.println("我是 B1"); } } class B2{ say(){ system.out.println("我是 B2"); } } class Client{ public static void main(String[] args){ Factory f=new Factory1(); //或者將這裡的建立邏輯也用簡單工廠的方式實現 A a=f.createA(); a.say(); B b=f.createB(); b.say(); //當我們的需求發生改變,或者新增系列 factory3...時,不需要修改後面四行的邏輯,只需要修改第一行——Factory f=new Factory3();即可Factory3需要 implements Factory 介面,Factory3中的 A3需要implements A,B3需要implements B } }
Intent
定義了一個建立物件的介面,但由子類決定要例項化哪個類。工廠方法把例項化操作推遲到子類。
Class Diagram
在簡單工廠中,建立物件的是另一個類,而在工廠方法中,是由子類來建立物件。
下圖中,Factory 有一個 doSomething() 方法,這個方法需要用到一個產品物件,這個產品物件由 factoryMethod() 方法建立。該方法是抽象的,需要由子類去實現。
Implementation
public abstract class Factory {
abstract public Product factoryMethod();
public void doSomething() {
Product product = factoryMethod();
// do something with the product
}
}
public class ConcreteFactory extends Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}
public class ConcreteFactory1 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct1();
}
}
public class ConcreteFactory2 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct2();
}
}