简单工厂属于创建型模式,但不在GoF23种设计模式行列中,具体参考上篇博文设计模式(一):开篇中的模式分类,但他确是我们学习计算机编程最早接触的一种模式,也是我们在不知不觉中使用的模式。
1.基本定义
由工厂对象决定创建出哪一种类型的产品实例。看到这句话可能有点懵,说白了就是我提供一个接口,你传一个类型参数给我,我返给你一个对应的实例。
1.1优点
只需要传递正确的参数,就可以获取到产品实例,不关心具体的创建过程细节。
1.2缺点
工厂类职责实现过重,增加新的类型时,需要修改工厂类。违法开闭原则(对扩展开发,对修改关闭)。从而对既有功能模块带来修改风险。
2.coding
下面我们以车辆生产作为背景介绍简单工厂模式,虚拟的情况不用对号入座。工厂有若干个生产线,可以生产各种汽车,比如奔驰、别克、法拉利等等。根据订单情况,选择生产。代码如下:
2.1 v0.1版
抽象类生产线
package com.xxfamly.creational.simpleFactory;
/**
* 生产线抽象类
*/
public abstract class Pipeline {
public abstract void produce();
}
奔驰生产线继承抽象类
package com.xxfamly.creational.simpleFactory;
/**
* 奔驰生产线
*/
public class BenzPipeline extends Pipeline {
@Override
public void produce() {
System.out.println("生产奔驰汽车");
}
}
别克生产线继承抽象类
package com.xxfamly.creational.simpleFactory;
/**
* 别克生产线
*/
public class BuickPipeline extends Pipeline {
@Override
public void produce() {
System.out.println("生产别克汽车");
}
}
法拉利生产线继承抽象类
package com.xxfamly.creational.simpleFactory;
/**
* 法拉利生产线
*/
public class FerrariPipeline extends Pipeline {
@Override
public void produce() {
System.out.println("生产法拉利汽车");
}
}
客户端指定生产车型,我们这个系列的博文统一使用Client.java作为客户端例子。
package com.xxfamly.creational.simpleFactory;
/**
* 模拟客户端
*/
public class Client {
public static void main(String[] args) {
//生产法拉利
Pipeline p1 = new FerrariPipeline();
p1.produce();
//生产奔驰
Pipeline p2 = new BenzPipeline();
p2.produce();
//生产别克
Pipeline p3 = new BuickPipeline();
p3.produce();
}
}
运行结果
com.xxfamly.creational.simpleFactory.Client
生产法拉利汽车
生产奔驰汽车
生产别克汽车
Process finished with exit code 0
可以看到程序根据客户端的指令生产了3种汽车。
我们来看一下UML类图
可以看到客户端同时依赖所有的生产线,如果任何生产线发生变动都会引发客户端的调整,客户端过多的关注生产细节,不利于系统扩展和集中维护。
2.2 v0.2版
我们来调整一下,增加一个工厂类,负责生产细节。
package com.xxfamly.creational.simpleFactory;
/**
* 生产线工厂类
*
* 根据传入类型返回具体的产品实例
*/
public class PipelineFactory {
public Pipeline getPiteline(String type){
if("benz".equalsIgnoreCase(type)){
return new BenzPipeline();
}else if("buick".equalsIgnoreCase(type)){
return new BuickPipeline();
}else if("ferrari".equalsIgnoreCase(type)){
return new FerrariPipeline();
}else{
return null;
}
}
}
调整一下客户端代码
package com.xxfamly.creational.simpleFactory;
/**
* 模拟客户端
*/
public class Client {
public static void main(String[] args) {
/* //生产法拉利
Pipeline p1 = new FerrariPipeline();
p1.produce();
//生产奔驰
Pipeline p2 = new BenzPipeline();
p2.produce();
//生产别克
Pipeline p3 = new BuickPipeline();
p3.produce();*/
//简单工厂模式,违反开闭原则
PipelineFactory pipelineFactory = new PipelineFactory();
Pipeline p1 = pipelineFactory.getPiteline("benz");
if (p1 == null) return;
p1.produce();
Pipeline p2 = pipelineFactory.getPiteline("buick");
if (p2 == null) return;
p2.produce();
Pipeline p3 = pipelineFactory.getPiteline("ferrari");
if (p3 == null) return;
p3.produce();
}
}
运行结果与v0.1版本一致。
查看UML类图
从类图中,客户端只依赖工厂类,生产的细节隐藏起来了,解放客户端编程的代码量。同时便于系统扩展和集中维护。但是我们也从中能看到工厂的职责是非常繁重的,且违反开闭原则这点我们前文提到了。
2.3 V0.3版
我们利用Java的反射机制,改进工厂类,使之能够符合开闭原则,如下。
简单工厂进阶类
package com.xxfamly.creational.simpleFactory;
/**
* 简单工厂进阶类
*/
public class PipeFactoryAdvance {
public Pipeline getPiteline(Class clazz){
try {
return (Pipeline)clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
客户端类改造
package com.xxfamly.creational.simpleFactory;
/**
* 模拟客户端
*/
public class Client {
public static void main(String[] args) {
/* //生产法拉利
Pipeline p1 = new FerrariPipeline();
p1.produce();
//生产奔驰
Pipeline p2 = new BenzPipeline();
p2.produce();
//生产别克
Pipeline p3 = new BuickPipeline();
p3.produce();*/
/*//简单工厂模式,违反开闭原则
PipelineFactory pipelineFactory = new PipelineFactory();
Pipeline p1 = pipelineFactory.getPiteline("benz");
if (p1 == null) return;
p1.produce();
Pipeline p2 = pipelineFactory.getPiteline("buick");
if (p2 == null) return;
p2.produce();
Pipeline p3 = pipelineFactory.getPiteline("ferrari");
if (p3 == null) return;
p3.produce();*/
//简单工厂进阶,对修改关闭,对扩展放开
PipeFactoryAdvance p = new PipeFactoryAdvance();
Pipeline p1 = null;
p1 = p.getPiteline(BenzPipeline.class);
if(p1 == null) return;
p1.produce();
p1 = p.getPiteline(BuickPipeline.class);
if(p1 == null) return;
p1.produce();
p1 = p.getPiteline(FerrariPipeline.class);
if(p1 == null) return;
p1.produce();
}
}
运行结果
生产奔驰汽车
生产别克汽车
生产法拉利汽车
Process finished with exit code 0
运行结果与V0.1,V0.2一致,看UML类图
从类图中可以看出,此时工厂类不依赖具体的生产线,再扩展一个生产线也不要调整工厂类,符合开闭原则,反射反射程序员最爱。