什么是工厂模式
富士康生产电子设备,它是个工厂,电子设备是产品。
一汽集团生产汽车,它是个工厂,汽车是产品。
大连造船厂生产航母,它是个工厂,航母是产品。
我翻阅了很多书籍,也浏览了很多博客,总结一下,工厂模式大致可以细分为三种:
- 工厂方法模式
- 简单工厂模式
- 抽象工厂模式
这么多年,一直有个问题萦绕在我心里,从未得到过能让我信服的答案:
简单工厂模式、抽象工厂模式,这两者到底有什么区别?
最近,我又系统的复习了一遍设计模式,没有局限于设计模式的经典之作《设计模式之禅》,我仿佛开悟了一般,看到了自己多年来想要的答案。
我觉的很多书籍、很多博客,阅读量多的,点赞量高的,对这个问题的认识和解答普遍都是错误的,自己没整明白,也误导了很多后来人。
我把自己的理解写下来,与大家分享探讨,希望能够帮助那些和我有着同样问题的人,找到答案,让你们自己信服。
工厂方法模式
设计模式是解决具体问题的通用思路,它的思想是面向对象,无关于任何具体的编程语言。
工厂方法模式,就是把一个静态方法当作创建对象的工厂,用Java
代码表示:
public class Product {}
public class Factory {
// 工厂方法
public static Product createProduct() {
return new Product();
}
}
工厂方法模式的特点:不需要创建工厂实例,就有工厂的能力。
上面的工厂方法只支持创建一类对象,如果要创建多类不同的对象,有两种变通方案:
- 工厂方法带参数,用参数区分要创建的不同类对象
- 每类对象分配一个工厂方法
如果是强类型的面向对象语言,选择方案1
就会有个约束,不同类型的对象需要有个共同的基类,因为强类型语言的方法返回值类型是唯一的。
两种方案的代码实现:
public class Product {} // 对象基类
public class Product1 extends Product {} // 对象1
public class Product2 extends Product {} // 对象2
public class Factory {
// 方案1:用工厂方法参数区分不同类型对象
public static Product createProduct(int type) {
if (type == 1) return new Product1();
else if (type == 2) return new Product2();
else return null;
}
// 方案2:一类对象一个工厂方法
public static Product1 createProduct1() {
return new Product1();
}
public static Product2 createProduct2() {
return new Product2();
}
}
网上很多人误以为方案2
就是简单工厂模式,这种理解是不对的。
简单工厂模式
简单工厂模式是工厂方法模式的升级版。
它俩的不同点:简单工厂模式需要有工厂实例对象。
它俩的共同点:工厂返回的都是具体的产品对象。
设计模式有六大基本原则,其中有个“接口隔离原则”,在这个原则的约束下,通常还会为工厂类和产品类定义统一的接口。
下面用Java
代码来展示分析三种业务场景,应用简单工厂模式,最后一种场景就是很多人以为的抽象工厂模式,但其实不是!
场景一:一个工厂,生产一种产品
// 产品类
public class Product {}
// 工厂接口
public interface IFactory{
Product createProduct();
}
// 简单工厂模式
public class Factory implements IFactory{
@Override
public Product createProduct() {
return new Product();
}
}
这段代码看着规范,但也冗余,可以考虑用上面说的工厂方法模式简化它。
场景二:多个工厂,生产同种类型不同型号的产品
// 产品
public interface Product {}
public class Product1 implements Product {}
public class Product2 implements Product {}
// 工厂
public interface IFactory {
Product createProduct();
}
public class Factory1 implements IFactory {
@Override
public Product createProduct() {
return new Product1();
}
}
public class Factory2 implements IFactory {
@Override
public Product createProduct() {
return new Product2();
}
}
这样的代码范式,没有异议,所有人都认可它是简单工厂模式。
场景三:多个工厂,生产不同类型不同型号的产品
这个场景下,产品就拿手机、电脑举例,两个工厂分别是华为工厂、小米工厂,分别生产:华为手机、华为电脑、小米手机、小米电脑。
// 产品接口
public interface Mobile {}
public interface Computer {}
// 手机产品
public class HuaWeiMobile implements Mobile {}
public class XiaoMiMobile implements Mobile {}
// 电脑产品
public class HuaWeiComputer implements Computer {}
public class XiaoMiComputer implements Computer {}
// 工厂接口
public interface IFactory {
Mobile createMobile();
Computer createComputer();
}
// 华为工厂
public class HuaWeiFactory implements IFactory {
@Override
public Mobile createMobile() { return new HuaWeiMobile(); }
@Override
public Computer createComputer() { return new HuaWeiComputer(); }
}
// 小米工厂
public class XiaoMiFactory implements IFactory {
@Override
public Mobile createMobile() { return new XiaoMiMobile(); }
@Override
public Computer createComputer() { return new XiaoMiComputer(); }
}
很多人会认为这种实现就是抽象工厂模式,理由大概都是从两个方面论证:
- 不同的产品族概念:华为产品族、小米产品族
- 不同的维度看产品:从功能角度分手机、电脑,从商家角度分华为、小米
其实,这两方面的理由表达的是同一个意思,无论哪个,都不成立。因为这都是从人的主观意识出发,站在不同视角看产品分类罢了。这样的理由根本就没有涉及到设计模式的核心要点。
试想一下:难道大名鼎鼎的简单工厂模式,就是工厂少一点,产品类型少一点这么简单的吗?这就是我多年的疑惑。
场景一到场景三的演变,工厂多了,产品多了,代码复杂了,但工厂模式的核心是没有变化的:
- 工厂本身有实例化的对象
- 工厂创建的都是具体产品
具体产品指的是Mobile
和Computer
的子类实例化对象,被工厂用一个个的new
关键字生产出来。
抽象产品指的是Mobile
和Computer
本身的产品概念,它是抽象工厂模式的核心要点。
抽象工厂模式
抽象工厂模式的核心要点是:
- 工厂具有实例化对象
- 工厂生产抽象产品
怎么理解“工厂生产抽象产品”这句话呢?
还拿上面的场景三举例,意思就是:一个工厂生产出来的手机可能是华为的,也可能是小米的;同理,生产出来的电脑可能是华为的,也可能是小米的。
它和简单工厂模式的区别是:简单工厂生产的产品具有确定性,抽象工厂生产的产品具有不确定性。
这个不确定性就是抽象工厂的含义!工厂生产出来的产品,我不确定它是华为手机还是小米手机,但我知道它是个手机。
分析到这里,代码实现就是多种多样的,总体上,应该类似于这样:
public interface Product {}
// 产品接口
public interface Mobile extends Product{}
public interface Computer extends Product{}
// 手机产品
public class HuaWeiMobile implements Mobile {}
public class XiaoMiMobile implements Mobile {}
// 电脑产品
public class HuaWeiComputer implements Computer {}
public class XiaoMiComputer implements Computer {}
// 工厂接口
public interface IFactory {
Mobile createMobile();
Computer createComputer();
}
// 没有具体的华为工厂、小米工厂,只有一个生产手机、电脑的工厂
public class Factory implements IFactory {
private Class<? extends Product> mobileClass;
private Class<? extends Product> computerClass;
public Factory(Class<? extends Product> mobileClass, Class<? extends Product> computerClass) {
this.mobileClass = mobileClass;
this.computerClass = computerClass;
}
@Override
public Mobile createMobile() {
try {
return (Mobile) mobileClass.newInstance();
} catch (Exception e) {
return null;
}
}
@Override
public Computer createComputer() {
try {
return (Computer) computerClass.newInstance();
} catch (Exception e) {
return null;
}
}
}
其实,这个例子和这段代码还不足以完全表现出抽象工厂模式的价值。
抽象工厂的重点是对工厂进行高度抽象化,避免自身成为一个具体的工厂。
抽象化不应该涉及具体,但它应该能够表达逻辑!
所以,抽象工厂解决的问题应该是具体产品的生产逻辑,也就是说,抽象工厂负责实现一类产品的生产逻辑,但是不负责具体产品的生产。
手机、电脑,都是由很多的零部件组装出来的,可以设计一个抽象工厂,负责组装再加工的流程,具体的零部件去其它地方“采购”,把所有的零部件扔到抽象工厂里面,工厂就能给我们生产出来手机、电脑。
抽象工厂不属于华为,也不属于小米,但它能生产手机、电脑。至于手机、电脑到底是华为的,还是小米的,取决于工厂里的零部件从谁家“采购”。
只要组装再加工的生产流程不变,抽象工厂就可以垄断所有手机、电脑的生产市场,而不用关心具体是谁家的品牌。
假如现在新增需求,我要生产苹果的手机、电脑,怎么办呢?好办,抽象工厂不变,把零部件的“采购”渠道换成苹果的,就能生产出苹果的手机、电脑。
看到没,这才是抽象工厂模式真正的威力所在!
总结
总体而言,工厂模式就两个核心要点:
- 有没有具体的工厂实例
- 生产的产品是具体的,还是抽象的
有工厂实例,生产抽象产品,就是抽象工厂模式。
有工厂实例,生产具体产品,就是简单工厂模式。
没有工厂实例,生产具体产品,就是工厂方法模式。
没有工厂实例,生产抽象产品,也是工厂方法模式。
如果按这样的划分规则,工厂模式是不是就应该有四种了呢?
再来看看最开始的问题:简单工厂模式、抽象工厂模式,这两者到底有什么区别?
我想答案应该是这样的:简单工厂生产具体产品,新增一种产品,就需要新增一个工厂;抽象工厂生产抽象产品,新增一种产品,不需要新增工厂。