1. 程式人生 > >【設計模式學習筆記】 之 簡單工廠模式

【設計模式學習筆記】 之 簡單工廠模式

們的 設置 inpu ride args forname property 簡單 是不是

簡介:工廠模式(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 }

輸出:

打開配置文件第一行,關掉第二行

全家出遊!
奔馳出行,請勿追尾
玩的愉快

打開配置文件第二行,關掉第一行

全家出遊!
本田上路,請勿拋磚
玩的愉快

總結:

  簡單工廠模式:

  1. 私有化工廠類的構造方法
  2. 根據配置文件的變化或者傳參的變化,動態返回一個該參數代表的類的實例
  3. 擁有一個getInstance的方法

【設計模式學習筆記】 之 簡單工廠模式