摘要
今天,我们来学习一下设计模式中的工厂模式,不过,就这样直接讲可能会有点闷,所以,在介绍工厂模式之前,我们先来看一个女娲造人的神话故事。东汉《风俗通》记录了这样一则神话故事:“开天辟地,未有人民,女娲搏黄土做人”,说的就是女娲造人的故事。女娲是如何造人的呢?
过程大概是这样的:首先,女娲采集黄土捏成人的形状,然后放到八卦炉中烧制,最后放到大地上生长。由于女娲是第一次做人,经验不足,对火候的把控不准确,柴火放多了,火太旺,七七四十九天之后,造好拿出来一看,人都被烤黑了,于是,黑种人就诞生了;然后女娲开始少放柴火,小火慢炖,又过了七七四十九天,拿出来一看,人还没烤熟呢,于是,白种人就诞生了;最后,女娲有经验了,把控好火的大小,就这样又过了七七四十九天,打开八卦炉一看,嗯,这次就对了,于是黄种人就诞生了。
那么我们可以用程序来实现这个有意思的女娲造人功能吗?答案是当然可以实现!接下来,我们就通过工厂模式来实现女娲造人这个功能。
一、前言
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替 new 操作的一种模式。它是创建型模式,其目的是将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性。一般工厂模式可以分为三类:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性,接下来我们将对他们逐一了解。
二、工厂模式详解
1、简单工厂模式
1.1、定义
简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离,降低系统的耦合度,使得两个修改起来相对容易些,当以后实现改变时,只需要修改工厂类即可。
1.2、UML 图
首先,我们先确定好有哪些对象。经过分析,该过程涉及三个对象:女娲、八卦炉和三种肤色的人。女娲相当于调用方 Client,八卦炉相当于生产人的工厂,三种肤色的人肯定都是人类,所以我们定义一个人类接口,让三种肤色的人实现这个接口。分析完毕,我们画出了如下的 UML 图:
1.3、代码实现
产品类:
public interface Human {
//打印肤色
public void printColor();
//说话
public void talk();
}
public class BlackHuman implements Human {
public void printColor() {
System.out.println("黑种人");
}
public void talk() {
System.out.println("黑种人说话了!!!");
}
}
public class WhiteHuman implements Human {
public void printColor() {
System.out.println("白种人");
}
public void talk() {
System.out.println("白种人说话了!!!");
}
}
public class YellowHuman implements Human {
public void printColor() {
System.out.println("黄种人");
}
public void talk() {
System.out.println("黄种人说话了!!!");
}
}
工厂类:
package com.top.luodiab.www.factory;
import com.top.luodiab.www.entity.Human;
import com.top.luodiab.www.entity.impl.BlackHuman;
import com.top.luodiab.www.entity.impl.WhiteHuman;
import com.top.luodiab.www.entity.impl.YellowHuman;
public class HumanFactory {
public Human createHuman(Class clazz){
if (clazz.equals(BlackHuman.class)){
return new BlackHuman();
}else if (clazz.equals(WhiteHuman.class)){
return new WhiteHuman();
}else if (clazz.equals(YellowHuman.class)){
return new YellowHuman();
}else{
return null;
}
}
}
用户类:
package com.top.luodiab.www.client;
import com.top.luodiab.www.entity.impl.BlackHuman;
import com.top.luodiab.www.entity.impl.WhiteHuman;
import com.top.luodiab.www.entity.impl.YellowHuman;
import com.top.luodiab.www.factory.HumanFactory;
public class NvWa {
public static void main(String[] args) {
HumanFactory factory = new HumanFactory();
System.out.println("开始创造黑种人.....");
BlackHuman blackHuman = (BlackHuman)factory.createHuman(BlackHuman.class);
blackHuman.printColor();
blackHuman.talk();
System.out.println("开始创造白种人.....");
WhiteHuman whiteHuman = (WhiteHuman)factory.createHuman(WhiteHuman.class);
whiteHuman.printColor();
whiteHuman.talk();
System.out.println("开始创造黄种人.....");
YellowHuman yellowHuman = (YellowHuman)factory.createHuman(YellowHuman.class);
yellowHuman.printColor();
yellowHuman.talk();
}
}
运行结果:
1.4、优缺点
优点:
- 将对象的创建交给专门的工厂类负责,实现了对象的创建和对象的使用分离
缺点:
- 工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,违反了开闭原则
2、工厂方法模式
2.1、定义
工厂方法模式的定义为:定义一个用于创建对象的接口或抽象类,让子类决定实例化哪一个类。
2.2、UML 图
2.3、代码实现
产品类:
public interface Human {
//打印肤色
public void printColor();
//说话
public void talk();
}
public class BlackHuman implements Human {
public void printColor() {
System.out.println("黑种人");
}
public void talk() {
System.out.println("黑种人说话了!!!");
}
}
public class WhiteHuman implements Human {
public void printColor() {
System.out.println("白种人");
}
public void talk() {
System.out.println("白种人说话了!!!");
}
}
public class YellowHuman implements Human {
public void printColor() {
System.out.println("黄种人");
}
public void talk() {
System.out.println("黄种人说话了!!!");
}
}
工厂类:
public abstract class AbstractHumanFactory {
public abstract Human createHuman();
}
public class BlackHumanFactory extends AbstractHumanFactory {
@Override
public Human createHuman() {
return new BlackHuman();
}
}
public class WhiteHumanFactory extends AbstractHumanFactory {
@Override
public Human createHuman() {
return new BlackHuman();
}
}
public class YellowHumanFactory extends AbstractHumanFactory {
@Override
public Human createHuman() {
return new BlackHuman();
}
}
用户类:
public class NvWa {
public static void main(String[] args) {
AbstractHumanFactory factory = null;
System.out.println("开始创造黑种人.....");
factory = new BlackHumanFactory();
Human blackHuman = factory.createHuman();
blackHuman.printColor();
blackHuman.talk();
System.out.println("开始创造白种人.....");
factory = new WhiteHumanFactory();
Human whiteHuman = factory.createHuman();
whiteHuman.printColor();
whiteHuman.talk();
System.out.println("开始创造黄种人.....");
factory = new YellowHumanFactory();
Human yellowHuman = factory.createHuman();
yellowHuman.printColor();
yellowHuman.talk();
}
}
运行结果:
2.4、优缺点
优点:
- 遵循了开闭原则,扩展性极强。比如现在要增加一个绿皮肤的人类,我们只需要增加一个创建绿皮肤人类的工厂,这个工厂继承自抽象工厂即可,不需要改变原有代码,可维护性高
缺点:
- 增加了类的数量,当有成千上万个类型的产品时,就需要有成千上万个工厂类来生产这些产品。使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖
3、抽象工厂模式
3.1、定义
抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象。
不过,乍一听,我们可能有点懵,所以我们先来了解一下产品族与产品等级
产品族:
一个人种下面的所有性别人;例如白种人下面的男人,女人 称为白种人的产品族
产品等级:
多个人种下面的同性别人;例如白种人和黑种人下面的男人,称为一个产品等级
类图分析:
以下图为例,有男人和女人两种性别人,有白种人和黑种人两种人种,两种人种都可以由女娲捏出男人和女人。
- 有男人和女人两种性别人,定义两个抽象类接口
- 白种人和黑种人都有这两种性别人,所以有 4 个实现类
- 现在需要创建白种人和黑种人的工厂类,先将工厂类进行抽象成抽象类,里面有创建两个性别人的方法,返回的是性别人的抽象类
- 创建白种人和黑种人的工厂实现类,继承工厂类接口,实现创建各自性别人的方法
- 客户端调用时,直接用工厂接口类创建需要的工厂,拿到对应的性别人
3.2、UML 图
3.3、代码实现
Human 接口及其两个实现类:
public interface Human {
//打印肤色
public void printColor();
}
public abstract class Man implements Human {
public void pringGender(){
System.out.println("性别为男");
}
public abstract void printColor();
}
public abstract class Woman implements Human {
public void pringGender(){
System.out.println("性别为女");
}
public abstract void printColor();
}
三种肤色的男人和女人:
public class BlackMan extends Man {
@Override
public void printColor() {
System.out.println("黑皮肤");
}
}
public class BlackWoman extends Woman {
@Override
public void printColor() {
System.out.println("黑皮肤");
}
}
public class WhiteMan extends Man {
@Override
public void printColor() {
System.out.println("白皮肤");
}
}
public class WhiteWoman extends Woman {
@Override
public void printColor() {
System.out.println("白皮肤");
}
}
public class YellowMan extends Man {
@Override
public void printColor() {
System.out.println("黄皮肤");
}
}
public class YellowWoman extends Woman {
@Override
public void printColor() {
System.out.println("黄皮肤");
}
}
抽象工厂代码:
public abstract class AbstractHumanFactory {
//抽象工厂
public abstract Man createMan();
public abstract Woman createWoman();
}
具体工厂代码:
public class BlackHumanFactory extends AbstractHumanFactory {
@Override
public Man createMan() {
return new BlackMan();
}
@Override
public Woman createWoman() {
return new BlackWoman();
}
}
public class WhiteHumanFactory extends AbstractHumanFactory {
@Override
public Man createMan() {
return new WhiteMan();
}
@Override
public Woman createWoman() {
return new WhiteWoman();
}
}
public class YellowHumanFactory extends AbstractHumanFactory {
@Override
public Man createMan() {
return new YellowMan();
}
@Override
public Woman createWoman() {
return new YellowWoman();
}
}
女娲使用工厂创造人类:
public class NvWa {
public static void main(String[] args) {
AbstractHumanFactory factory = null;
System.out.println("开始创造黑种人.....");
factory = new BlackHumanFactory();
Man blackMan = factory.createMan();
blackMan.pringGender();
blackMan.printColor();
Woman blackWoman = factory.createWoman();
blackWoman.pringGender();
blackWoman.printColor();
System.out.println("开始创造白种人.....");
factory = new WhiteHumanFactory();
Man whiteMan = factory.createMan();
whiteMan.pringGender();
whiteMan.printColor();
Woman whiteWoman = factory.createWoman();
whiteWoman.pringGender();
whiteWoman.printColor();
System.out.println("开始创造黄种人.....");
factory = new YellowHumanFactory();
Man yellowMan = factory.createMan();
yellowMan.pringGender();
yellowMan.printColor();
Woman yellowWoman = factory.createWoman();
yellowWoman.pringGender();
yellowWoman.printColor();
}
}
运行结果:
3.4、优缺点
优点:
- 增加固定类型产品的不同具体工厂比较方便。例如现在女娲要创造一个绿皮肤的人,只需要再创建一个绿皮肤人的工厂继承抽象工厂就可以了
- 一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象(将一个系列的产品统一一起创建)
缺点:
- 类图有点复杂,可读性没有工厂方法模式那么好
- 增加了系统的抽象性和理解难度
3.5、优缺举例:新增一个产品族或产品等级会怎样?
拓展一个产品族
我们会发现,拓展一个产品族是非常困难的,例如产品族中新增一个中性人,也就是说白种人和黑种人现在可以生产中性人了,如下图所示(红色框和文本为新增一个产品族需要做的事),对顶层的工厂接口类也要修改,这是非常麻烦的
拓展一个产品等级
如果扩展一个产品等级,例如新增一个男人,也就是说新增一个人种来生产男人,如下图所示(红色框为新增一个产品等级需要做的事),新增一个产品等级不用修改原来的代码,符合 OCP 原则,这是非常舒服的
三、总结
以上就是我个人关于 设计模式——工厂模式 的一些笔记,如果有什么问题,可以将问题发我邮箱 luodiab@126.com,欢迎各位的意见。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于