【設計模式學習筆記】 之 簡單工廠模式
簡介:工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,拒絕客服端程序員通過new創建需要的實例,並且是通過使用一個共同的接口來指向新創建的對象,即接口引用指向實現類對象,是多態的靈活運用。
舉例1【未使用工廠模式】:
一個家庭中有多輛汽車,這個家庭想去出遊,需要先傳一個Car到Family中持有,才能出遊。
首先考慮多輛汽車,都有同樣的run方法,抽取公共接口如下:
1 package com.mi.simplefactory.simplefactory1;2 3 /** 4 * 汽車接口 5 */ 6 public interface Car { 7 8 public void run(); 9 }
現在假設有兩輛汽車(多輛同理),HondaCar (本田)和 BenzCar(奔馳),都實現了Car接口
1 package com.mi.simplefactory.simplefactory1; 2 3 public class HondaCar implements Car{ 4 5 @Override 6 public void run() { 7 System.out.println("本田上路,請勿拋磚");8 } 9 10 11 }
1 package com.mi.simplefactory.simplefactory1; 2 3 public class BenzCar implements Car { 4 5 @Override 6 public void run() { 7 System.out.println("奔馳出行,請勿追尾"); 8 } 9 10 }
這裏需要一個Family類,她應持有一個私有Car的引用,使用Family的構造方法進行初始化Car引用,Family中有一個travel方法
1 package com.mi.simplefactory.simplefactory1; 2 3 public class Family { 4 5 private Car car; 6 7 public Family(Car car) { 8 this.car = car; 9 } 10 public Family() {} 11 public void travel() { 12 System.out.println("全家出遊!"); 13 car.run(); 14 System.out.println("玩的愉快"); 15 } 16 }
編寫測試類,發現需要通過new 不同的Car實現類實例就可以改變汽車實際類型
1 package com.mi.simplefactory.simplefactory1; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 /** 7 * 問題來了:family換個車必須要new不同的車,必須要修改代碼,重新編譯才能 8 * 正確更換汽車,還有,可不可以不用new的方式獲取汽車? 9 */ 10 // Family family = new Family(new HondaCar()); 11 Family family = new Family(new BenzCar()); 12 family.travel(); 13 } 14 }
輸出:
先註掉11行,解開10行,運行
全家出遊!
本田上路,請勿拋磚
玩的愉快
註掉10,解開11,運行
全家出遊!
奔馳出行,請勿追尾
玩的愉快
舉例2【分支判斷實現工廠】:
這裏Car接口和Car接口的實現類不變,重寫一個Family類,為car引用設置get set方法,去掉有參構造方法
1 package com.mi.simplefactory.simplefactory2; 2 3 import com.mi.simplefactory.simplefactory1.Car; 4 5 public class Family { 6 7 private Car car; 8 9 public Family() {} 10 public void travel() { 11 System.out.println("全家出遊!"); 12 car.run(); 13 System.out.println("玩的愉快"); 14 } 15 public Car getCar() { 16 return car; 17 } 18 public void setCar(Car car) { 19 this.car = car; 20 } 21 22 23 }
創建一個工廠,通過獲取汽車的類名為參數,內部if分支判斷,實例化內部持有的car引用,還有有一個靜態方法getInstance(),獲取該 Car的實例
1 package com.mi.simplefactory.simplefactory2; 2 3 import com.mi.simplefactory.simplefactory1.BenzCar; 4 import com.mi.simplefactory.simplefactory1.Car; 5 import com.mi.simplefactory.simplefactory1.HondaCar; 6 7 public class Factory { 8 9 private Car car; 10 public Factory(String carName) { 11 if(carName.equals("BenzCar")) { 12 this.car = new BenzCar(); 13 } 14 if(carName.equals("HondaCar")) { 15 this.car = new HondaCar(); 16 } 17 } 18 public Car getInstance() { 19 return car; 20 } 21 22 }
測試類
1 package com.mi.simplefactory.simplefactory2; 2 3 import com.mi.simplefactory.simplefactory1.Car; 4 5 public class Test { 6 7 public static void main(String[] args) { 8 /** 9 * 此包中的factory可以不使用new的方式獲取汽車 10 */ 11 Family family = new Family(); 12 // Factory factory = new Factory("HondaCar"); 13 Factory factory = new Factory("BenzCar"); 14 Car car = factory.getInstance(); 15 family.setCar(car); 16 family.travel(); 17 } 18 19 }
輸出就不貼出來了,和之前的輸出是一樣的。
這個例子,我們的確通過一個工廠去得到我們需要的實例,但是,如果汽車特別的多,我們是不是需要寫無數個分支判斷啊?代碼很冗長,效率不高,於是我們想到了通過反射來實現,於是有了第三個例子
舉例3【通過反射方式】:
學過反射的同學都應該明白,我們可以通過一個properties配置文件去動態改變代碼中產生的代碼,這是種可以不改變代碼的情況下使用的很方便的方式
創建一個properties配置文件:config.properties,(這裏直接寫好了兩個,方便切換)
#Car=com.mi.simplefactory.simplefactory1.BenzCar
Car=com.mi.simplefactory.simplefactory1.HondaCar
創建Factory類
1 package com.mi.simplefactory.simplefactory3; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Properties; 6 7 import com.mi.simplefactory.simplefactory1.Car; 8 9 public class Factory { 10 11 private Factory() {} //拒絕客服端程序員new工廠對象,僅提供靜態方式訪問 12 public static Properties configs = new Properties(); 13 static { 14 //獲取配置文件流 15 InputStream in = Factory.class.getResourceAsStream("config.properties"); 16 try { 17 //properties讀取文件輸入流 18 configs.load(in); 19 } catch (IOException e) { 20 e.printStackTrace(); 21 //靜態方法/塊中的異常是捕獲不到的 22 throw new RuntimeException(e); 23 } 24 } 25 public static Car getInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException { 26 //從配置文件中取出Car鍵的值:汽車類的全路徑 27 String carName = configs.getProperty("Car"); 28 //反射獲取Class對象 29 Class<?> clazz = Class.forName(carName); 30 //反射新建實例 31 Car car = (Car)clazz.newInstance(); 32 return car; 33 } 34 35 }
以上代碼是在這個Factory類被ClassLoader load的時候自動讀取配置文件,並在配置文件中get(key)的方式獲得key對應的value,查看Java docs文檔可以看出Properties類是一種Map的實現,通過forName反射出我們需要的類的Class對象(在文件中查找我們需要的 類.class 文件),通過newInstance()方法獲取該類的實例,相當於new,這樣我們就能動態的通過修改配置文件,而無需修改代碼,無需重新編譯即可改變代碼中的對象。
下邊是測試類
1 package com.mi.simplefactory.simplefactory3; 2 3 import com.mi.simplefactory.simplefactory1.Car; 4 import com.mi.simplefactory.simplefactory2.Family; 5 6 public class Test { 7 8 public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 9 Family family = new Family(); 10 Car car = Factory.getInstance(); 11 family.setCar(car); 12 family.travel(); 13 } 14 }
輸出:
打開配置文件第一行,關掉第二行
全家出遊!
奔馳出行,請勿追尾
玩的愉快
打開配置文件第二行,關掉第一行
全家出遊!
本田上路,請勿拋磚
玩的愉快
總結:
簡單工廠模式:
- 私有化工廠類的構造方法
- 根據配置文件的變化或者傳參的變化,動態返回一個該參數代表的類的實例
- 擁有一個getInstance的方法
【設計模式學習筆記】 之 簡單工廠模式