抽象工厂模式在我的上一篇博文里已经详细介绍过,地址:https://blog.youkuaiyun.com/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值的方式也不够灵活,也可以做进一步的更改,比如把这个值写在配置文件中,然后去配置文件取值,这样就更加灵活了。
此处不再赘述在配置文件取值的代码了。
说在后面:
本文主要是小猫看《大话设计模式》的笔记式的记录,方便以后查阅。
文中的例子是小猫自己想的,仅供参考,代码也有不够详尽的地方,主要是为了说明设计模式的思想。