AbstractHumanFactory是一個抽象類,定義了一個八卦爐都具有的整體功能,
HumanFactory爲實現類,完成具體的任務:創建人類;
Human接口是人類的總稱,其三個實現類分別爲三類人種;
NvWa類是一個場景類,負責模擬這個場景,執行相關的任務。
我們定義的每個人種都有兩個方法:getColor(獲得人的皮膚顏色)和talk(交談),其源代碼如代碼清單1所示。
代碼清單1 人類總稱
public interface Human {
// 每個人種皮膚都有相應的顏色
public void getColor();
// 人類會說話
public void talk();
}
接口Human是對人類的總稱,每個人種都至少具有兩個方法,黑色人種、***人種、白色人種的代碼分別如代碼清單2、3、4所示。
代碼清單2 黑色人種
public class BlackHuman implements Human {
@Override
public void getColor() {
System.out.println("黑種人的皮膚顏色是黑色的!");
}
@Override
public void talk() {
System.out.println("黑人會說話,一般聽不懂。");
}
}
代碼清單3 ***人種
public class YellowHuman implements Human {
@Override
public void getColor() {
System.out.println("黃種人的皮膚顏色是***的!");
}
@Override
public void talk() {
System.out.println("黃種人會說話,一般說的都是雙字節。");
}
}
代碼清單4 白色人種
public class WhiteHuman implements Human {
@Override
public void getColor() {
System.out.println("白種人的皮膚顏色是白色的!");
}
@Override
public void talk() {
System.out.println("白種人會說話,一般都是單字節。");
}
}
所有的人種定義完畢,下一步就是定義一個八卦爐,然後燒製人類。我們想象一下,女媧最可能給八卦爐下達什麼樣的生產命令呢?應該是“給我生產出一個***人種(YellowHuman類)”,而不會是“給我生產一個會走、會跑、會說話、皮膚是***的人種”,因爲這樣的命令增加了交流的成本,作爲一個生產的管理者,只要知道生產什麼就可以了,而不需要事物的具體信息。通過分析,我們發現八卦爐生產人類的方法輸入參數類型應該是Human接口的實現類,這也解釋了爲什麼類圖上的AbstractHumanFactory抽象類中createHuman方法的參數爲Class類型。其源代碼如代碼清單5所示。
代碼清單5 抽象人類創建工廠
public abstract class AbstractHumanFactory {
public abstract Human createHuman(Class<? extends Human> clazz);
}
注意,我們在這裏採用了JDK 1.5的新特性:泛型(Generic),通過定義泛型對createHuman的輸入參數產生兩層限制:
必須是Class類型;
必須是Human的實現類;
其中的“?”表示的是,只要實現了Human接口的類都可以作爲參數,泛型是JDK 1.5中的一個非常重要的新特性,它減少了對象間的轉換,約束其輸入參數類型,對Collection集合下的實現類都可以定義泛型。有關泛型詳細知識,請參考相關的Java語法文檔。
目前女媧只有一個八卦爐,其實現了生產人類的方法,如代碼清單6所示。
代碼清單6 人類創建工廠
public class HumanFactory extends AbstractHumanFactory {
@Override
public Human createHuman(Class<? extends Human> clazz) {
// 定義一個生產的人種
Human human = null;
try {
human = (Human) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
System.out.println("人種生成錯誤!");
}
return human;
}
}
人種有了,八卦爐也有了,剩下的工作就是女媧採集黃土,然後命令八卦爐開始生產,其過程如代碼清單7所示。
代碼清單7 女媧類
public class NvWa {
public static void main(String[] args) {
// 聲明陰陽八卦爐
AbstractHumanFactory YinYangLu = new HumanFactory();
// 女媧第一次造人,火候不足,缺陷產品
System.out.println("--造出的第一批人是白色人種--");
Human whiteHuman = YinYangLu.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.talk();
// 女媧第二次造人,火候過足,又是次品,
System.out.println("\n--造出的第二批人是黑色人種--");
Human blackHuman = YinYangLu.createHuman(BlackHuman.class);
blackHuman.getColor();
blackHuman.talk();
// 第三次造人,火候剛剛好,優品!***人種
System.out.println("\n--造出的第三批人是***人種--");
Human yellowHuman = YinYangLu.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.talk();
}
}
人種有了,八卦爐有了,負責生產的女媧也有了,激動人心的時刻到來了,我們運行一下,結果如下所示。
--造出的第一批人是白色人種--
白色人種的皮膚顏色是白色的!
白色人種會說話,一般都是但是單字節。
--造出的第二批人是黑色人種--
黑色人種的皮膚顏色是黑色的!
黑人會說話,一般人聽不懂。
--造出的第三批人是***人種--
***人種的皮膚顏色是***的!
***人種會說話,一般說的都是雙字節。
哇,人類的生產過程就展現出來了!這個世界就熱鬧起來了,黑人、白人、黃人都開始活動了,這也正是我們現在的真實世界。以上就是工廠方法模式(沒錯,對該部分有疑問,請繼續閱讀下去)。
工廠方法模式的定義
工廠方法模式使用的頻率非常高,在我們日常的開發中總能見到它的身影。其定義爲:
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses。定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
工廠方法模式的通用類圖如圖2所示。
圖2 工廠方法模式通用類圖
在工廠方法模式中,
抽象產品類Product負責定義產品的共性,實現對事物最抽象定義;
Creator爲抽象創建類,也就是抽象工廠,具體如何創建產品類是由具體的實現工廠ConcreteCreator完成的。
工廠方法模式的變種較多,我們來看一個比較實用的通用源碼。
抽象產品類代碼如代碼清單8所示。
代碼清單8 抽象產品類
public abstract class Product {
// 產品類的公共方法
public void method1() {
// 業務邏輯處理
}
// 抽象方法
public abstract void method2();
}
具體的產品類可以有多個,都繼承於抽象產品類,其源代碼如代碼清單9所示。
代碼清單9 具體產品類
public class ConcreteProduct1 extends Product {
@Override
public void method2() {
// 業務邏輯處理
}
}
public class ConcreteProduct2 extends Product {
@Override
public void method2() {
// 業務邏輯處理
}
}
抽象工廠類負責定義產品對象的產生,源代碼如代碼清單10所示。
代碼清單10 抽象工廠類
public abstract class Creator {
/**
* 創建一個產品對象,其輸入參數類型可以自行設置 通常爲String、Enum、Class等,當然也可以爲空
*
* @param clazz
* @return
*/
public abstract Product createProduct(Class<? extends Product> clazz);
}
具體如何產生一個產品的對象,是由具體的工廠類實現的,如代碼清單11所示。
代碼清單11 具體工廠類
public class ConcreteCreator extends Creator {
@Override
public Product createProduct(Class<? extends Product> clazz) {
Product product = null;
try {
product = (Product) Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
// 異常處理
}
return product;
}
}
場景類的調用方法如代碼清單12所示。
代碼清單12 場景類
public class Client {
public static void main(String[] args) {
Creator creator = new ConcreteCreator();
Product product = creator.createProduct(ConcreteProduct1.class);
/*
* 繼續業務處理
*/
}
}
該通用代碼是一個比較實用、易擴展的框架,讀者可以根據實際項目需要進行擴展。
工廠方法模式的擴展
縮小爲簡單工廠模式
我們這樣考慮一個問題:一個模塊僅需要一個工廠類,沒有必要把它產生出來,使用靜態的方法就可以了,根據這一要求,我們把上例中的AbstarctHumanFactory修改一下,
最佳實踐
工廠方法模式在項目中使用得非常頻繁,以至於很多代碼中都包含工廠方法模式。該模式幾乎盡人皆知,但不是每個人都能用得好。熟能生巧,熟練掌握該模式,多思考工廠方法如何應用,而且工廠方法模式還可以與其他模式混合使用(例如模版方法模式、單例模式、原型模式等),變化出無窮的優秀設計,這也正是軟件設計和開發的樂趣所在。