2.2 工廠方法模式
** 定義:**
定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
2.2.1 工廠方法模式
優點:
- 良好的封裝性,代碼結構清晰。對象創建只需要知道產品的類名,不用知道對象創建的過程
- 擴展性非常優秀。增加產品,只要適當修改具體的工廠類或擴展一個工廠類
- 屏蔽產品類。產品類的實現如何變化,調用者都不用關心。
ex:使用JDBC連接數據庫,數據庫從MySQL切換到Oracle,只需要改動一下驅動的名稱。
- 典型的解耦擴建。高層模塊只需要知道產品的抽象類,符合迪米特法則;只依賴產品類的抽象,符合依賴倒置原則;可用產品的子類代替父類,符合里氏替換原則
使用場景:
- 工廠方法模式是new一個對象的替代品,所以在需要new 對象的地方都可以使用。但需慎重考慮是否要增加一個工廠類進行管理,以免增加代碼復雜度。
- 需要靈活的、可擴展的框架時,可以考慮。
ex:設計一個連接郵件服務器的框架,POP3、IMAP、HTTP三協議的連接方式作為產品類來處理。當某些郵件服務器擴展了webservice協議時,只需要增加webservice的產品類
- 可以用在異構項目中。
ex:通過webservice與一個非java的項目交互,雖然webServcie號稱是可以做到異構系統的同構化,但是在實際的開發中,會碰到類型問題、WSDL文件的支持問題等。從WSDL中產生的對象都認為是一個產品,然后由具體工廠管理,減少與外圍的耦合。
- 可以使用在測試框架中。
ex:測試類A,就需要把與A有聯系的B也同時產生出來。可以使用工廠方法模式把B虛擬出來,避免A與B 的耦合。可對比JMock、EasyMock。
代碼:
//抽象產品類
abstract class Product{
public void method1(){}//公共方法
public abstract void method2();//抽象方法
}
//具體產品類
class CreateProduct1 extends Product{
public void method2(){}
}
class CreateProduct2 extends Product{
public void method2(){}
}
//抽象工廠
abstract class Creator{
public abstract <T extends Product> T createProduct(Class<T> c);
}
//具體工廠
class CreateCreator extends Creator{
public <T extends Product> T createProduct(Class<T> c){
Product p = null;
try{
p = (Product) Class.forName(c.getName()).newInstance();
}catch(Exception e){
}
return (T)p;
}
}
//場景類
class Client{
public static void main(String[] args){
Creator creator = new CreateCreator();
Product p = creator.createProduct(CreateProduct1.class);
}
}
2.2.2 擴展-簡單工廠模式
需求:
一個模塊僅需要一個工廠類,沒有必要把它new 出來,使用靜態方法就可以了。
代碼:
//具體工廠
class CreateCreator {
public static <T extends Product> T createProduct(Class<T> c){
Product p = null;
try{
p = (Product) Class.forName(c.getName()).newInstance();
}catch(Exception e){
}
return (T)p;
}
}
//場景類
class Client{
public static void main(String[] args){
Product p = CreateCreator.createProduct(CreateProduct1.class);
}
}
2.2.3 擴展-多個工廠類
需求:
項目比較復雜,初始化一個對象比較費力(設定初始值),所有產品類都放在一個工廠方法中進行初始化會使代碼結構不清晰。
如:一個抽象產品類有5個具體實現,每個實現類的初始化方法都不相同,如果寫在一個方法中,會導致該方法巨大無比。
解決:
為每個產品定義一個創造者,然后由調用者自己去選擇與哪個工廠方法關聯。
結果:
每個產品類對應一個具體工廠,好處是工廠類的職責清晰且結構簡單,但可擴展性和可維護性有影響:
如果要擴展一個產品類,就要建立一相應的工廠,這樣就增加了擴展的難度;具體工廠類和產品類的數目相同,維護需要考慮兩者的關系。
代碼:
//具體工廠1
class CreateCreator1 extends Creator{
public Product createProduct(){
return new CreateProduct1();
}
}
//具體工廠2
class CreateCreator2 extends Creator{
public Product createProduct(){
return new CreateProduct2();
}
}
//場景類
class Client{
public static void main(String[] args){
Product cp1 = (new CreateCreator1()).createProduct();
Product cp2 = (new CreateCreator2()).createProduct();
}
}
2.2.4 替代單例模式
擴展:
項目中可以建一個單例構造器,只要輸入單例的類型就可以獲得唯一的實例
代碼:
class Singleton{
private Singleton(){}
public void doSomething(){}
}
class SingletonFactory{
private static Singleton single;
static{
try {
Class cls = Class.forName(Singleton.class.getName());
Constructor constructor = cls.getDeclaredConstructor();
constructor.setAccessible(true);
single = (Singleton) constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Singleton getSingleton(){
return single;
}
}
2.2.5 延遲初始化
延遲初始化:lazy initialization 一個對象被消費完畢,不立即釋放,工廠類保持其初始狀態,等待再次被使用。
擴展:
限制某個產品類的最大實例化數量,可以通過判斷map中的已有對象數量來實現。參考:JDBC連接數據庫,可以設置一個MaxConnection最大連接數,該數量就是內存中的最大實例化的數量。
其它應用場景:
對象初始化比較復雜,如硬件訪問,涉及多方面的交互,可以通過延遲加載降低對象的產生和銷毀帶來的復雜性。