Java設計模式:工廠模式

pan_jinquan發表於2016-07-24

Java設計模式:工廠模式

    【尊重原創,轉載請註明出處】http://blog.csdn.net/guyuealian/article/details/52015589
     工廠模式的意圖:定義一個介面來建立物件,但是讓子類來決定哪些類需要被例項化。工廠方法把例項化的工作推遲到子類中去是實現。 工廠模式可以根據客戶的需要,定義一個工廠類專門負責建立類的例項。
一、先來看一下普通的設計模式:
public interface FruitInterface{
	public void Colour();
}
public class Apple implements FruitInterface{
	@Override
	public void Colour() {
		System.out.println("---蘋果的顏色是紅色的---");
	}
}
public class Orange implements FruitInterface{
	@Override
	public void Colour() {
		System.out.println("---橘子的顏色是黃色的---");
	}
}
public class FruitTest { //客戶端程式
	public static void main(String[] args) {
		FruitInterface f = new Apple();
		f.Colour();
	}       
}
     上面的客戶端程式Fruit f = new Apple();可以獲得蘋果的顏色,但若要獲得橘子的顏色,就需要在客戶端增加橘子的例項化程式碼,並顯式的呼叫;這樣並不利於後期的維護和管理;決解的方法是新建一個工廠,通過IF判斷語句,根據客戶端需要,例項化對應的物件,實現對顏色的統一管理

二、新增加一個Factory作為過渡端,並通過過渡端取得介面的例項化物件,這個過渡端就是工廠類:
//工廠類,用於生產物件
public class Factory {
	public FruitInterface getColour(String key) {
		FruitInterface f = null;// 定義介面物件,通過過子類例項化
		if ("Apple".equals(key)) {
			f = new Apple();// 通過Apple類例項化介面
		} else if ("Orange".equals(key)) {
			f = new Orange();// 通過Orange類例項化介面
		}
		return f;
	}
}
//客戶端程式
public class FruitTest {
	public static void main(String[] args) {
	//(1)普通設計模式,客戶端需要顯式的實現化物件
        // FruitInterface f = new Apple();
	// f.Colour();
        //(2)工廠設計模式,客戶端僅需要改變輸入引數,增加新類時需要修改工廠類
		Factory factory = new Factory();
		FruitInterface colour = factory.getColour("Apple");
		colour.Colour();
	}
}
       與普通的設計模式相比,工廠模式例項過程是在工廠類完成的,而不是在客戶端的程式碼中實現的,客戶端只需改變輸入引數(改變引數Apple),即可獲得對用的例項物件。通過工廠類,可以避免客戶端顯式的呼叫顏色這個方法,從而降低了客戶端程式與產品物件的耦合。

三、繼續改進:工廠類中通過IF判斷語句,可以選擇性地建立需要的例項化物件;但這時,如果需要增加新的水果,如葡萄時;需要改動兩個地方:不僅需要新建一個葡萄的類,還需要在工廠類中新增判斷和例項化語句。為了降低程式的耦合,這時需要用到反射的機制來動態的建立物件。
      繼續改進Factory類:

//工廠類,用生產物件
public class Factory {
	// 根據型別來建立物件
	public FruitInterface getColour(String key) {
		FruitInterface f = null;// 定義介面物件,同過子類例項化
		if ("Apple".equals(key)) {
			f = new Apple();// 通過Apple類例項化介面
		} else if ("Orange".equals(key)) {
			f = new Orange();// 通過Orange類例項化介面
		}
		return f;
	}

	// 通過反射,根據類的名稱來生產物件
	public FruitInterface getColourByClass(String className) throws Exception {
		FruitInterface fruit = (FruitInterface) Class.forName(className)
				.newInstance();
		return fruit;
	}
}
//客戶端程式
public class FruitTest {
	public static void main(String[] args) throws Exception {
		//(1)普通設計模式,客戶端需要顯式的實現化物件
        // FruitInterface f = new Apple();
		// f.Colour();
        //(2)工廠設計模式,客戶端僅需要改變輸入引數
		//Factory factory = new Factory();
		// FruitInterface colour = factory.getColour("Apple");
		// colour.Colour();
        //(3)通過反射的工廠設計模式,客戶端僅需要改變輸入引數
        Factory factory = new Factory();
		FruitInterface colour = factory.getColourByClass("com.fruit.Apple");
		colour.Colour();
	}
}
      通過反射的工廠設計模式,當有新增加的類時,如增加葡萄這種水果,這時只需要新建一個子類Grape,並實現介面FruitInterface的方法Colour(),客戶端僅需要改變輸入引數(更改引數com.fruit.Apple即可),而Factory類不需要修改任何程式碼。
PS:通過反射的方式建立物件,效能會要稍微低些,通常情況下,沒有必要使用反射來建立物件,只有當程式需要動態建立某個類的物件時才考慮使用反射。
四、升級改進:
      通過反射的工廠設計模式已經可以大大的降低程式的耦合,但這時客戶端輸入引數時,需要輸入整個完整的全限定類名(即完整的包名+類名),如果需要多個例項物件,這時客戶端需要進行多次的更改引數;解決的方法是,把需要配置的引數以鍵值物件(Key-Value)的形式儲存在屬性檔案中。而客戶端只需要知道Key值就可以呼叫。
      新建屬性檔案type.properties,其以key-value的形式儲存輸入引數和包名的對應關係
Apple=com.fruit.Apple
Orange=com.fruit.Orange
Grape=com.fruit.Grape
     有了屬性檔案,就需要獲取和解釋該屬性檔案的類PropertiesReader 
//properties檔案的讀取工具
public class PropertiesReader {
	public Map<String, String> getProperties() {
		Properties props = new Properties();
		Map<String, String> map = new HashMap<String, String>();
		try {
			InputStream in = getClass().getResourceAsStream("type.properties");
			props.load(in);
			Enumeration en = props.propertyNames();
			while (en.hasMoreElements()) {
				String key = (String) en.nextElement();
				String property = props.getProperty(key);
				map.put(key, property);
				// System.out.println(key + "  " + property);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}
}
      在工廠類Factory中,新增加一個方法getColourByClassKey,獲取屬性檔案,並根據value值來生產物件:
//通過獲取屬性檔案,根據value值來生產物件
	public FruitInterface getColourByClassKey(String key) throws Exception {
		Map<String, String> map = new PropertiesReader().getProperties();
		FruitInterface fruit = (FruitInterface) Class.forName(map.get(key))
				.newInstance();
		return fruit;
	}
     這時客戶端只需要傳入一個key值即可:
// (4)傳入Key值,獲取屬性檔案
FruitInterface colour = factory.getColourByClassKey("Apple");
	colour.Colour();
     這種升級後的工廠設計模式,把需要的對映的關係都儲存到屬性檔案中,客戶端只需要知道屬性檔案中key值,就可以實現功能;維護時,只需要修改屬性檔案,把key值告訴客戶端使用者即可。


相關文章