13. 工厂方法模式
定义
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
通俗理解
老王家里开了一家“老王杯子塑料厂”,做的是杯子。前几年生意好,杯子卖得越来越火,畅销海内外。随着市场的饱和,老王发现,他们家的杯子越来越卖不动了,不管是方形的杯子,还是圆形的杯子,亦或椭圆形的杯子,消费者越来越不买账,塑料厂的生意也因此节节败落。眼瞧着工厂都快倒闭了,敏锐的老王发现,市场上的塑料饭盒还是一片蓝海,老王为了能够让塑料厂存活下去,为了厂里面的几百人的饭碗,决定进入这个市场。
虽然这个市场前景一片大好,但是可不是想进就进。塑料饭盒和杯子的制造工艺相同却在细节上面有所偏差,例如杯子只需要简单的冲模就可以了,但是饭盒的容量更大,除了冲模之外还要加一些加强筋,那样饭盒才不容易碎掉。老王呀,为了这样的一件事情,真是愁得都变成了地中海了。
于是他和厂里的技术人员交流如何去实现。技术人员和不像老王一样天天去邻居家,还是会研究一下技术问题的。他们一眼就看透了,虽然杯子和饭盒在工艺上有偏差,但是偏差并不大,原来的机器调整一下参数,开一条生产线就可以解决了。
有生意头脑的老王,成立了一家“老王饭盒塑料厂”,按照技术人员提出的建议开干起来。这不,不到一个月,老王就拿着他新颖的饭盒,杀进了这片蓝海当中了。
这个就是工厂方法模式,在原来的工厂上面,再开一家新的工厂,新旧两家工厂的工艺是相同的,但是产不同种类的产品。同时,每个工厂都有不同的生产线,即使是同一种种类的产品,也可以在一个工厂里面生产出不同类型的产品。就像老王的厂一样,一开始只有杯子厂,杯子厂产圆形、方形的杯子。后来杯子的市场不好了,借用杯子厂的机器,开了一家饭盒厂,生产圆形的、方形的饭盒,而不是在杯子厂上生产饭盒。
示例
程序以老王的杯子厂做示例子。
渣渣程序
按照昨天学的简单工厂模式,我们可以写出下面的程序:
产品基类
public abstract class BaseProduct {
public abstract String type();
}
杯子和饭盒
public class CircleCup extends BaseProduct {
@Override
public String type() {
return "这是一个圆杯子";
}
}
public class CircleLunchBox extends BaseProduct {
@Override
public String type() {
return "这是一个圆饭盒";
}
}
工厂
public class Factory {
public static BaseProduct build(String type) {
switch (type){
case "circleCup": return new CircleCup();
case "squareCup": return new SquareCup();
case "circleLunchBox": return new CircleLunchBox();
case "squareLunchBox": return new SquareLunchBox();
default:return null;
}
}
}
调用方
public class Main {
public static void main(String[] args) {
BaseProduct cup = Factory.build("circleCup");
System.out.println(cup.type());
BaseProduct lunchBox = Factory.build("circleLunchBox");
System.out.println(lunchBox.type());
}
}
上面的程序存在以下问题:
- Factory的责任太大,他不仅仅要生产杯子,也要生产饭盒,如果需要生产牙刷,那么还得在Factory里面加代码,明显违反开闭原则。
- Factory的责任不明确,杯子生产和饭盒生产明显不是一条生产线上的东西,但是他们却在一个工厂里面生产了。
优化
既然所有的问题都是因工厂而起,那么这些问题的解决,就只能交给工厂了。工厂方法就是为了解决这个问题而生的。
类图
程序
工厂类接口IFactory
public interface IFactory {
BaseProduct build(String type);
}
杯子工厂类
public class CupFactory implements IFactory {
public BaseProduct build(String type) {
switch (type){
case "circleCup": return new CircleCup();
case "squareCup": return new SquareCup();
default:return null;
}
}
}
杯子和饭盒抽象类BaseProduct
public abstract class BaseProduct {
public abstract String type();
}
杯子具体类
public class CircleCup extends BaseProduct {
@Override
public String type() {
return "这是一个圆杯子";
}
}
调用方
public class Main {
public static void main(String[] args) {
IFactory cupFactory = new CupFactory();
BaseProduct cup = cupFactory.build("circleCup");
System.out.println(cup.type());
IFactory lunchBoxFactory = new LunchBoxFactory();
BaseProduct lunchBox = lunchBoxFactory.build("circleLunchBox");
System.out.println(lunchBox.type());
}
}
优点
- 工厂不单单只有一个,可以根据基类的工厂扩展不同的工厂,用户选择工厂生产产品就可以,不需要关心生产的细节。
- 加入新的产品时候,不需要像原来的简单工厂一样,修改工厂方法,而是通过添加新的工厂和新的产品来解决,符合开闭原则。
缺点
- 加产品的时候要加产品类,还要加工厂类,类好多好庞大。
应用场景
- 用户不关心产品的生产过程,只知道相应的工厂就可以生产产品
- 需要扩展不同的工厂的情况下使用,如果使用简单工厂的时候,如果发现工厂类频繁改动,那么就可以考虑使用工厂方法模式。