抽象工廠模式在我的上一篇博文裏已經詳細介紹過,地址: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值的方式也不夠靈活,也可以做進一步的更改,比如把這個值寫在配置文件中,然後去配置文件取值,這樣就更加靈活了。
此處不再贅述在配置文件取值的代碼了。
說在後面:
本文主要是小貓看《大話設計模式》的筆記式的記錄,方便以後查閱。
文中的例子是小貓自己想的,僅供參考,代碼也有不夠詳盡的地方,主要是爲了說明設計模式的思想。