工厂方法模式或许是程序员在日常开发过程中最常听到或用到的设计模式之一了。但是,我们真的了解它的来龙去脉吗?
文章目录
案例展示——FactoryMethod很简单
我们来考虑这样一种场景:有一家宝马汽车制造厂商需要生产一批宝马汽车,在这批宝马汽车中,需要生产X5系列1000辆,X6系列5000辆,X7系列10000辆,根据这个需求我们可以使用工厂方法模式来完成,类图设计如下:
具体分析:
-
BaomaCar 是一个抽象的宝马汽车接口,定义了不同系列宝马汽车的共性特征:颜色,价格
-
BaomaX5,BaomaX6,BaomaX7实现了BaomaCar接口,可以做自己的逻辑实现
-
AbstractBaomaCarFactory是一个生产宝马汽车的抽象工厂,定义了一个抽象方法:createBaomaCar
-
BaomaCarFactory是生产宝马汽车的工厂,它继承了AbstractBaomaCarFactory,并实现了createBaomaCar 方法
其中各个类的代码实现实现如下:
//抽象工厂
public abstract class AbstractBaomaCarFactory {
//生产汽车的抽象工厂
public abstract <T extends BaomaCar> T createBaomaCar(Class<T> c);
}
//具体的工厂
public class BaomaCarFactory extends AbstractBaomaCarFactory{
public <T extends BaomaCar> T createBaomaCar(Class<T> c) {
//定义一个BaomaCar的接口
BaomaCar baomaCar = null;
try {
//生产一辆BaomaCar
baomaCar = (T)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
System.out.println("汽车生产出错。。。");
}
return (T)baomaCar;
}
}
//宝马汽车接口
public interface BaomaCar {
//汽车的颜色
void getColor();
//汽车的价格
void getPrice();
}
public class BaomaX5 implements BaomaCar {
public void getColor() {
System.out.println("我是X5,我是宝石蓝。。。");
}
public void getPrice() {
System.out.println("我要卖40w。。。");
}
}
public class BaomaX6 implements BaomaCar {
public void getColor() {
System.out.println("我是X6,我是宝石红。。。");
}
public void getPrice() {
System.out.println("我要卖60w。。。");
}
}
public class BaomaX7 implements BaomaCar {
public void getColor() {
System.out.println("我是X7,我是宝石黑。。。");
}
public void getPrice() {
System.out.println("我要卖80w。。。");
}
}
在一个场景类中生产宝马汽车:
public class Client {
public static void main(String[] args) {
//声明汽车工厂
AbstractBaomaCarFactory factory = new BaomaCarFactory();
//生产X5
System.out.println("生产x5。。。。");
BaomaCar x5 = factory.createBaomaCar(BaomaX5.class);
//生产X6
System.out.println("生产x6。。。。");
BaomaCar x6 = factory.createBaomaCar(BaomaX6.class);
//生产X7
System.out.println("生产x7。。。。");
BaomaCar x7 = factory.createBaomaCar(BaomaX7.class);
System.out.println("=============Show Time===========");
x5.getColor();
x5.getPrice();
x6.getColor();
x6.getPrice();
x7.getColor();
x7.getPrice();
}
}
//结果如下:
生产x5。。。。
生产x6。。。。
生产x7。。。。
=============Show Time===========
我是X5,我是宝石蓝。。。
我要卖40w。。。
我是X6,我是宝石红。。。
我要卖60w。。。
我是X7,我是宝石黑。。。
我要卖80w。。。
回归本源——什么是FactoryMethod?
通过上面的案例展示,我们大体了解了工厂方法模式的用法。现在我们就要具体讨论一下工厂方法模式是什么?我们为什么使用它?
FactoryMethod的定义
定义: 定义一个用于创建对象的接口,让子类决定去实例化那一个类。工厂方法使一个类的实例化延迟到子类。工厂方法模式的通用类图如下:
对每一个结构的说明:
-
Product负责定义产品的共性特征,实现对事物最抽象的定义,比如刚刚的BaomaCar
public abstract class Product { //产品类的公共方法 public void method1() { //业务逻辑 } //抽象方法 public abstract void method2(); }
-
具体的产品类可以有多个,都继承(实现)于抽象产品类
public class ConcreateProduct1 extends Product { public void method() { //业务逻辑 } } public class ConcreateProduct2 extends Product { public void method() { //业务逻辑 } }
-
抽象工厂类负责定义产品对象的产生
public abstract class Creater { //创建一个产品对象,输入参数类型可以自行设计 public abstract <T extends Product> T createProduct(Class<T> c); }
-
具体的工厂类产生一个产品对象
public class ConcreateCreater extends Creater { public <T extends Product> T createProduct(Class<T> c) { Product product = null; try { product = (Product)Class.forName(c.getName()).newInstance(); }catch(Exception e) { //异常处理 } } }
-
场景类中使用
public class Client { public static void main(String[] args) { Creater creater = new ConcreateCreater(); Product product = creater.createProduct(ConcreateProduct1.class); //业务处理 } }
FactoryMethod的优点
-
良好的封装性,代码结构清晰。一个对象的创建是有条件约束的,如一个调用者需要一个具体的产品对象,只需要知道这个产品的类名就行,不用知道具体的创建过程,降低了模块之间的耦合性
-
具有很强的扩展性。就如我们刚刚的案例来说,如果汽车厂想推出一个新的系列,比如X9,那只需要新建一个BaomaX9类来实现BaomaCar接口就行,而工厂不需要任何改变就能完成系统的扩展。
-
屏蔽了产品类。产品类的实现如何变化,调用者都不需要关心,只需要关心产品的接口,只要接口保持不变,下层模块就能保持不变。例如:使用jdbc连接数据库时,对于选择MySQL还是Oracle,只需要改一个驱动的名称就行,不需要我们去管工厂是如何创建实例的。
-
工厂方法模式是一种解耦框架。
- 高层模块(client)只需要知道产品的抽象类,其他实现类不用关心,符合迪米特法则
- 对于不需要的模块不需要交流,只依赖产品类的抽象,符合依赖倒置原则
- 产品的子类可以替换父类,符合里式替换原则
FactoryMethod的扩展
1. 简单工厂模式(静态工厂模式)
一个模块仅需要一个工厂类,不需要做额外的扩展,这时只需要使用静态的方法就行,类图设计如下:
代码实现如下:
public class BaomaCarFactory{
//提供一个静态的方法
public static <T extends BaomaCar> T createBaomaCar(Class<T> c) {
//定义一个BaomaCar的接口
BaomaCar baomaCar = null;
try {
//生产一辆BaomaCar
baomaCar = (T)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
System.out.println("汽车生产出错。。。");
}
return (T)baomaCar;
}
}
//在场景类中的实现与结果
public class Client {
public static void main(String[] args) {=
//生产X5
System.out.println("生产x5。。。。");
BaomaCar x5 = BaomaCarFactory.createBaomaCar(BaomaX5.class);
//生产X6
System.out.println("生产x6。。。。");
BaomaCar x6 = BaomaCarFactory.createBaomaCar(BaomaX6.class);
//生产X7
System.out.println("生产x7。。。。");
BaomaCar x7 = BaomaCarFactory.createBaomaCar(BaomaX7.class);
System.out.println("=============Show Time===========");
x5.getColor();
x5.getPrice();
x6.getColor();
x6.getPrice();
x7.getColor();
x7.getPrice();
}
}
//结果如下:
生产x5。。。。
生产x6。。。。
生产x7。。。。
=============Show Time===========
我是X5,我是宝石蓝。。。
我要卖40w。。。
我是X6,我是宝石红。。。
我要卖60w。。。
我是X7,我是宝石黑。。。
我要卖80w。。。
2. 升级为多个工厂
在项目比较复杂的时候,所有的产品类都放到工厂方法中进行初始化会使代码结构不清晰。例如案例中三个系列车型,可能不同系列的车型在初始化时会有不同的要求,这时用一个工厂就不合适了,我们需要扩展一下,增加三个对应的工厂来专门生产,这样会是代码结构更加清晰与容易扩展,类图设计如下:
代码实现如下:
public abstract class AbstractBaomaCarFactory {
//生产汽车的抽象工厂
public abstract BaomaCar createBaomaCar();
}
//对应系列的宝马车生产工厂
public class BaomaX5Factory extends AbstractBaomaCarFactory {
public BaomaCar createBaomaCar() {
return new BaomaX5();
}
}
public class BaomaX6Factory extends AbstractBaomaCarFactory {
public BaomaCar createBaomaCar() {
return new BaomaX6();
}
}
public class BaomaX7Factory extends AbstractBaomaCarFactory {
public BaomaCar createBaomaCar() {
return new BaomaX7();
}
}
3. 替代单例模式
单例模式的核心要求就是在内存中只有一个对象,通过工厂模式也可以只在内存中生产一个对象,类图设计如下:
直接看代码实现;
//单例类
public class Singleton {
//不允许通过new产生对象
private Singleton() {
}
public void doSomething() {
//业务逻辑
}
}
//负责生成单例的工厂类
public class SingletonFactory {
//内存中只有一个singleton对象
private static Singleton singleton;
//静态代码块,只被加载一次
static {
try {
Class c = Class.forName(Singleton.class.getName());
//获得无参构造器
Constructor constructor = c.getDeclaredConstructor();
//设置无参构造器是可访问的
constructor.setAccessible(true);
//产生一个实例对象
singleton = (Singleton)constructor.newInstance();
} catch (Exception e) {
System.out.println("有异常。。。");
}
}
//返回singleton对象
public static Singleton getSingleton() {
return singleton;
}
}
//场景类中运行测试
public class Client2 {
public static void main(String[] args) {
Singleton singleton1 = SingletonFactory.getSingleton();
Singleton singleton2 = SingletonFactory.getSingleton();
System.out.println(singleton1 == singleton2);
}
}
//结果
true //在内存中只有一个实例
4. 延迟初始化
一个对象被消费完毕后,并不立刻释放,工厂类保持其初始化状态,等待再次被使用。延迟初始化是FactoryMethod的一个扩展,其类图设计如下:
代码实现如下:
//延迟加载的工厂类
public class ProductFactory {
private static final Map<String, Product> prMap = new HashMap();
public static synchronized Product createProduct(String type) throws Exception {
Product product = null;
//如果map中已经有对象,则取出返回
if (prMap.containsKey(type)) {
product = prMap.get(type);
} else {
if (type.equals("product1")) {
product = new ConcreateProduct1();
} else {
product = new ConcreateProduct2();
}
//把对象放到map中缓存
prMap.put(type, product);
}
return product;
}
}
延迟加载框架是可扩展的,例如限制某一个产品类的最大实例化数量,可以通过Map中已有的对象数量来实现(jdbc连接数据库时,设置一个最大连接数)。
牛刀小试——FactoryMethod和TemplateMethod整合
工厂方法模式是一种非常灵活的设计模式,非常易于扩展,可以灵活的和其他设计模式进行混合使用而发挥巨大威力。如下将展示工厂方法模式和模板方法模式的整合。考虑这样的场景:延续上述的案例,我们现在已经生产好一批宝马汽车,但我们不知道它们的性能如何,需要我们去进行测试。思考一番我们知道,对于每一辆汽车来说,测试的步骤都是可以由父类定义好的(比如先发动,开启引擎,鸣笛,跑起来,关闭。。。),并不需要子类去改变,所以这就可以作为一种框架定义在父类中,对子类进行约束。下面是设计类图:
代码实现如下:
//生产对象的工厂没有任何变化
public abstract class AbstractBaomaCarFactory {
//生产汽车的抽象工厂
public abstract <T extends BaomaCar> T createBaomaCar(Class<T> c);
}
public class BaomaCarFactory extends AbstractBaomaCarFactory{
public <T extends BaomaCar> T createBaomaCar(Class<T> c) {
//定义一个BaomaCar的接口
BaomaCar baomaCar = null;
try {
//生产一辆BaomaCar
baomaCar = (T)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
System.out.println("汽车生产出错。。。");
}
return (T)baomaCar;
}
}
//抽象类,定义了共性特征
public abstract class BaomaCar {
//汽车的颜色
public abstract void getColor();
//汽车的价格
public abstract void getPrice();
//发动汽车
protected abstract void start();
//停止汽车
protected abstract void stop();
//鸣笛
protected abstract void alarm();
//启动引擎
protected abstract void engineBoom();
//钩子函数(hook):用来控制汽车是否鸣笛
protected boolean isAlarm() {
return true;
}
/**
* 定义一个模板方法,使子类按照定义的顺序执行
*/
public void run() {
//发动汽车
this.start();
//启动引擎
this.engineBoom();
//鸣笛
if (isAlarm()) {
this.alarm();
}
//停止汽车
this.stop();
}
}
public class BaomaX5 extends BaomaCar {
public void getColor() {
System.out.println("我是X5,我是宝石蓝。。。");
}
public void getPrice() {
System.out.println("我要卖40w。。。");
}
protected void start() {
System.out.println("我是X5,我要发动了。。。");
}
protected void stop() {
System.out.println("我要停止了。。。");
}
protected void alarm() {
System.out.println("我要鸣笛,前面的家伙绕道。。。");
}
protected void engineBoom() {
System.out.println("我要发动引擎了。。。");
}
}
public class BaomaX6 extends BaomaCar {
public void getColor() {
System.out.println("我是X6,我是宝石红。。。");
}
public void getPrice() {
System.out.println("我要卖60w。。。");
}
protected void start() {
System.out.println("我是X6,我要发动了。。。");
}
protected void stop() {
System.out.println("我要停止了。。。");
}
protected void alarm() {
System.out.println("我要鸣笛,前面的家伙绕道。。。");
}
protected void engineBoom() {
System.out.println("我要发动引擎了。。。");
}
}
public class BaomaX7 extends BaomaCar {
public void getColor() {
System.out.println("我是X7,我是宝石黑。。。");
}
public void getPrice() {
System.out.println("我要卖80w。。。");
}
protected void start() {
System.out.println("我是X7,我要发动了。。。");
}
protected void stop() {
System.out.println("我要停止了。。。");
}
protected void alarm() {
System.out.println("我要鸣笛,前面的家伙绕道。。。");
}
protected void engineBoom() {
System.out.println("我要发动引擎了。。。");
}
}
在一个场景中运行代码:
public class Client {
public static void main(String[] args) {
//声明汽车工厂
AbstractBaomaCarFactory factory = new BaomaCarFactory();
//生产X5
System.out.println("生产x5。。。。");
BaomaCar x5 = factory.createBaomaCar(BaomaX5.class);
//生产X6
System.out.println("生产x6。。。。");
BaomaCar x6 = factory.createBaomaCar(BaomaX6.class);
//生产X7
System.out.println("生产x7。。。。");
BaomaCar x7 = factory.createBaomaCar(BaomaX7.class);
System.out.println("=============Show Time===========");
x5.getColor();
x5.getPrice();
x6.getColor();
x6.getPrice();
x7.getColor();
x7.getPrice();
System.out.println();
System.out.println("==============Test Time==============");
System.out.println("====测试X5系列===");
x5.run();
System.out.println("====测试X6系列===");
x6.run();
System.out.println("====测试X7系列===");
x7.run();
}
}
//结果如下:
生产x5。。。。
生产x6。。。。
生产x7。。。。
=============Show Time===========
我是X5,我是宝石蓝。。。
我要卖40w。。。
我是X6,我是宝石红。。。
我要卖60w。。。
我是X7,我是宝石黑。。。
我要卖80w。。。
==============Test Time==============
====测试X5系列===
我是X5,我要发动了。。。
我要发动引擎了。。。
我要鸣笛,前面的家伙绕道。。。
我要停止了。。。
====测试X6系列===
我是X6,我要发动了。。。
我要发动引擎了。。。
我要鸣笛,前面的家伙绕道。。。
我要停止了。。。
====测试X7系列===
我是X7,我要发动了。。。
我要发动引擎了。。。
我要鸣笛,前面的家伙绕道。。。
我要停止了。。。
参考
《设计模式之禅》