工廠方法模式(Factory Method Pattern)。
定義:
定義一個用於建立物件的介面,讓子類決定例項化哪一個類。工廠方法使一個類的例項化延遲到其子類。
通用程式碼:
抽象產品類(Product)負責定義產品的共性
public abstract clas Product {
// 產品類的公共方法
public void method1(){
// 業務邏輯處理
}
// 抽象方法
public abstract void method2();
}
具體產品類(可以有多個,都繼承於抽象產品類)
pubilc class ConcreteProduct1 extends Product { public void method2(){ // 業務邏輯處理 } } pubilc class ConcreteProduct2 extends Product { public void method2(){ // 業務邏輯處理 } }
抽象工廠類(Creator為抽象建立類,也就是抽象工廠,負責定義產品)
public abstract class Creator {
/*
* 建立一個產品物件,其輸入引數型別可以自行設定
* 通常為String、Enum、class等,當然也可以為空
*/
public abstract <T extends Product> T createProduct(Class<T> c);
}
具體工廠類(具體如何產生一個產品的物件,是由具體的工廠類建立的)
public class ConcreteCreator extends Creator { public <T extends Product> T createProduct(Class<T> c) { Product product = null; try{ product = (Product) Class.forName(c.getName()).nextInstance(); } catch (Exception e) { // 異常處理 } return (T) product; } }
場景類
public class Client {
public static void main(String[] args) {
Creator creator = new ConcreteCreator();
Product product = creator.createProduct(ConcreteProduct1.class);
// 繼續業務處理
}
}
優點:
- 良好的封裝性,程式碼結構清晰。
- 工廠方法模式的擴充套件性非常優秀。
- 遮蔽產品類。
- 工廠方法模式時典型的解耦框架。
使用場景:
- 工廠方法模式是new一個物件的替代品,所以在需要生成物件的地方都可以使用,但是需要慎重的考慮是否增加一個工廠類進行管理,增加程式碼的複雜度。
- 需要靈活的、可擴充套件的框架時,可以考慮採用工廠方法模式。
- 工廠方法模式可以用在異構專案中。
- 可以使用在測試驅動開發的框架下。例如,測試一個類A,就需要把與類A有關聯關係的類B也同時產生出來,我們可以使用工廠方法模式把類B虛擬出來,避免類A與類B的耦合。目前由於JMock和EasyMock的誕生,該使用場景已經弱化了,讀者可以在遇到此種情況時直接考慮使用JMock或EazyMock。
工廠方法模式的擴充套件
1、縮小為簡單工廠模式
簡單工廠模式(Simple Factory Pattern),也叫作靜態工廠模式。在實際專案中,採用該方法的案例還是比較多的,其缺點是工廠類的擴充套件比較困難,不符合開閉原則,但它仍然是一個非常實用的設計模式。
2、升級為多個工廠類
在複雜的應用中一般採用多工廠的方法,然後再增加一個協調類,避免呼叫者與各個子工廠交流,協調類的作用是封裝子工廠了類,對高層模組提供統一的方法介面。
3、替代單例模式
負責生成單例的工廠類
public class SingletonFactory {
private static Singleton singleton;
static{
try{
Class cl = Class.forName(Singleton.class.getName());
// 獲得無參構造
Constructor constructor = cl.getDeclaredConstructor();
// 設定無參構造是可以訪問的
constructor.setAccessible(true);
// 產生一個例項物件
singleton = (Singleton) constructor.newInstance()
} catch (Exception e) {
// 異常處理
}
}
public static Singleton getSingleton() {
return singleton;
}
}
4、延遲初始化(Lazy initialization)
定義:一個物件被消費完畢後,並不立即釋放,工廠類保持其初始狀態,等待再次被使用。
延遲載入的工廠類
public class ProductFactory {
private static final Map<String , Product> prMap = new HashMap();
public static synchronized Product createProduct(String type) throws Exception {
Product product = null;
// 如果Map中已經有這個物件
if(prMap.containsKey(type)) {
product = prMap.get(type);
} else {
if("Product1".equals(type)) {
product = new ConcreteProduct1();
} else {
product = new ConcreteProduct2();
}
// 同時把物件放到快取容器中
prMap.put(type.product);
}
return product;
}
}
延遲載入框架時可以擴充套件的,例如限制某一個產品類的最大例項化數量,可以通過判斷Map中已有的物件數量來實現,這樣的處理是非常有意義的,例如JDBC連線資料庫,都會要求社會一個MaxConnections最大連線數量,該數量就是記憶體中最大例項化的數量。
延遲載入還可以用在物件初始化比較複雜的情況下,例如硬體訪問,涉及多方面的互動,則可以通過延遲載入降低物件的產生和銷燬帶來的複雜性。
最佳實踐:
工廠方法模式在專案中使用得非常頻繁,以至於很多程式碼中都包含工廠方法模式。該模式近乎人盡皆知,但不是每個人都能用得好。熟能生巧,熟練掌握該模式,多思考工廠方法如何應用,而且工廠方法模式還可以與其他模式混合使用(例如模板方法模式、單例模式、原型模式燈),變化出無窮的優秀設計,這也正是軟體設計和開發的樂趣所在。
案例(女媧造人):
人類總稱
public interface Human {
// 每個人種的面板都有相應的顏色
public void getColor();
// 人類會說話
public void talk();
}
黑色人種(黃色人種和白色人種省略)
public class BlackHuman implements Human {
public void getColor() {
System.out.println("黑色人種的面板顏色是黑色的!");
}
public void talk(){
System.out.println("黑人會說話,一般人聽不懂。");
}
}
抽象人類建立工廠
public abstract class AbstractHumanFactory {
public abstract <T extends Human> T createHuman(Class<T> c);
}
注意,我們在這裡採用了泛型(Generic),通過定義泛型對createHuman的輸入引數產生兩層限制:
- 必須是Class型別;
- 必須是Human的實現類。
其中的“T”表示的是,只要實現了Human介面的類都可以作為引數,泛型是JDK1.5中的一個非常重要的新特性,它減少了物件間的轉換,約束其輸入引數型別,對Collection集合下的實現類都可以定義泛型。
人類建立工廠
public class HumanFactory extends AbstractHumanFactory {
public <T extends Human> T createHuman(Class<T> c) {
// 定義一個生產的人種
Human human = null;
try {
// 產生一個人種
human = (T) Class.forName(c.getName()).newInstrance();
} catch (Exception e) {
System.out.println("人種生成錯誤!");
}
return (T) human;
}
}
女媧類
public class NvWa {
public static void main(String[] args) {
// 宣告陰陽八卦爐
AbstractHumanFactory YinYangLu = new HumanFactory();
// 產生黑人
Human blackHuman = YinYangLu.createHuman(BlackHuman .class);
blackHuman.getColor();
blackHuman.talk();
}
}
簡單工廠模式中的工廠類
public class HumanFactory {
public static <T extends Human> T createHuman(Class<T> c) {
// 定義一個生產的人種
Human human = null;
try {
// 產生一個人種
human = (T) Class.forName(c.getName()).newInstrance();
} catch (Exception e) {
System.out.println("人種生成錯誤!");
}
return (T) human;
}
}
女媧類
public class NvWa {
public static void main(String[] args) {
// 宣告陰陽八卦爐
AbstractHumanFactory YinYangLu = new HumanFactory();
// 產生黑人
Human blackHuman = HumanFactory.createHuman(BlackHuman .class);
blackHuman.getColor();
blackHuman.talk();
}
}
多工廠模式的抽象工廠類
public abstract class AbstractHumanFactory {
public abstract Human createHuman();
}
黑色人種的建立工廠實現(白色人種和黃色人種省略)
public class BlackHumanFactory extends AbstractHumanFactory {
public Human createHuman() {
return new BlackHuman();
}
}
女媧類
public class NvWa {
public static void main(String[] args) {
// 宣告陰陽八卦爐
AbstractHumanFactory YinYangLu = new HumanFactory();
// 產生黑人
Human blackHuman = (new BlackHumanFactory()).createHuman(BlackHuman .class);
blackHuman.getColor();
blackHuman.talk();
}
}