Java設計模式之抽象工廠模式的改進

抽象工廠模式在我的上一篇博文裏已經詳細介紹過,地址:https://blog.csdn.net/zaoan_2010/article/details/81987011

它的缺點主要體現在,需要新增產品時,做的改動比較多。針對這個缺點對抽象工廠模式做了一個改進。

1. 簡單工廠+抽象工廠

舉例說明:工廠需要生產中性筆和鉛筆,中性筆有晨光牌和得力牌的。現如果需要增加油筆的生產,則需要增加油筆的抽象產品類及兩個具體產品類(OilPike(油筆)、ChenGuangOilPike、DeLiOilPike),需要修改抽象工廠類及兩個具體工廠類(StationaryFactory、ChenGuangFactory、DeLiFactory),改動比較多。

現通過簡單工廠進行改進抽象工廠模式,去除StationaryFactory、ChenGuangFactory、DeLiFactory三個工廠類,用Stationary類替代,採用switch方法判斷並實例化。

老樣子,先上類圖

從類圖中就可以看出比抽象工廠簡化了好多,下面上代碼

抽象產品類

package main.mode.cxgcms2;

public abstract class GelPen {

	public abstract void product();
}

抽象產品類對應的具體產品類

package main.mode.cxgcms2;

public class ChenGuangGelPen extends GelPen{

	@Override
	public void product() {
		System.out.println("生產晨光牌中性筆");		
	}

}
package main.mode.cxgcms2;

public class DeLiGelPen extends GelPen{

	@Override
	public void product() {
		System.out.println("生產得力牌中性筆");
	}

}

抽象產品類2

package main.mode.cxgcms2;

public abstract class Pencil {

	public abstract void product();
}

抽象產品類2對應的具體產品類

package main.mode.cxgcms2;

public class ChenGuangPencil extends Pencil{

	@Override
	public void product() {
		System.out.println("生產晨光牌鉛筆");
	}

}
package main.mode.cxgcms2;

public class DeLiPencil extends Pencil{

	@Override
	public void product() {
		System.out.println("生產得力牌鉛筆");
	}

}

工廠類

package main.mode.cxgcms2;

public class Stationary {

	private static String type = "chenguang";//也可改爲deli
	
	public static GelPen productGelPen() {
		GelPen gelPen = null;
		switch(type){
		case("chenguang"): 
			gelPen = new ChenGuangGelPen();
			break;
		case("deli"):
			gelPen = new DeLiGelPen();
			break;
		default:
			System.out.println("沒有獲取到正確類型");
			break;
		}
		return gelPen;
	}
	
	public static Pencil productPencil() {
		Pencil pencil = null;
		switch(type){
		case("chenguang"): 
			pencil = new ChenGuangPencil();
			break;
		case("deli"):
			pencil = new DeLiPencil();
			break;
		default:
			System.out.println("沒有獲取到正確類型");
			break;
		}
		return pencil;
	}
	
}

測試類

package main.mode.cxgcms2;

public class FactoryTest {

	public static void main(String[] args) {
		//需要生產晨光牌的中性筆和鉛筆
		GelPen gelPen = Stationary.productGelPen();
		gelPen.product();
		
		Pencil pencil = Stationary.productPencil();
		pencil.product();
	}

}

測試結果:

可以看出,測試類中沒有出現任何晨光或得力的字眼,達到了解耦的目的。

當然,也可以把工廠類中自定義的字段type提出來作爲參數在調用時傳入,不過這樣就增加了調用方法(測試類)和工廠類的耦合性,不太好。

但簡單工廠改進的抽象工廠模式仍然有其弊端。
如果現在增加一個生產商真彩,則需要改動類Stationary,增加switch分支,違背了封閉-開放原則。

可以換一個更好的方式去進行類的實例化--反射。

這樣更靈活一些。

2. 反射+抽象工廠

這裏只是更改了實例化的方式,只需要改動Stationary類即可,其他類的代碼同上。

直接上代碼

package main.mode.cxgcms3;

public class Stationary {
	
	private static String url = "main.mode.cxgcms3";
	private static String type = "ChenGuang";//也可改爲DeLi
	
	public static GelPen productGelPen() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		String fullName = url + "." + type + "GelPen";
		Class<?> clazz =  Class.forName(fullName);
		GelPen gelPen = (GelPen) clazz.newInstance();
		return gelPen;
	}
	
	public static Pencil productPencil() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		String fullName = url + "." + type + "Pencil";
		Class<?> clazz =  Class.forName(fullName);
		Pencil pencil = (Pencil) clazz.newInstance();
		return pencil;
	}
	
}

可以看出,這樣就比上面switch方式靈活了一下,如果增加生產商真彩,只需要改動type值即可,下面的方法不需要做改動。

當然了,這種在類中取String值的方式也不夠靈活,也可以做進一步的更改,比如把這個值寫在配置文件中,然後去配置文件取值,這樣就更加靈活了。

此處不再贅述在配置文件取值的代碼了。

 

說在後面:

本文主要是小貓看《大話設計模式》的筆記式的記錄,方便以後查閱。

文中的例子是小貓自己想的,僅供參考,代碼也有不夠詳盡的地方,主要是爲了說明設計模式的思想。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章