设计模式——工厂模式

本贴最后更新于 432 天前,其中的信息可能已经时异事殊

摘要

今天,我们来学习一下设计模式中的工厂模式,不过,就这样直接讲可能会有点闷,所以,在介绍工厂模式之前,我们先来看一个女娲造人的神话故事。东汉《风俗通》记录了这样一则神话故事:“开天辟地,未有人民,女娲搏黄土做人”,说的就是女娲造人的故事。女娲是如何造人的呢?

过程大概是这样的:首先,女娲采集黄土捏成人的形状,然后放到八卦炉中烧制,最后放到大地上生长。由于女娲是第一次做人,经验不足,对火候的把控不准确,柴火放多了,火太旺,七七四十九天之后,造好拿出来一看,人都被烤黑了,于是,黑种人就诞生了;然后女娲开始少放柴火,小火慢炖,又过了七七四十九天,拿出来一看,人还没烤熟呢,于是,白种人就诞生了;最后,女娲有经验了,把控好火的大小,就这样又过了七七四十九天,打开八卦炉一看,嗯,这次就对了,于是黄种人就诞生了。

那么我们可以用程序来实现这个有意思的女娲造人功能吗?答案是当然可以实现!接下来,我们就通过工厂模式来实现女娲造人这个功能。

一、前言

工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替 new 操作的一种模式。它是创建型模式,其目的是将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性。一般工厂模式可以分为三类:

  • 简单工厂模式(Simple Factory)
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性,接下来我们将对他们逐一了解。

二、工厂模式详解

1、简单工厂模式

1.1、定义

简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离,降低系统的耦合度,使得两个修改起来相对容易些,当以后实现改变时,只需要修改工厂类即可。

1.2、UML 图

首先,我们先确定好有哪些对象。经过分析,该过程涉及三个对象:女娲、八卦炉和三种肤色的人。女娲相当于调用方 Client,八卦炉相当于生产人的工厂,三种肤色的人肯定都是人类,所以我们定义一个人类接口,让三种肤色的人实现这个接口。分析完毕,我们画出了如下的 UML 图:

01 工厂模式.jpg

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();

    }
}

运行结果:

02 工厂模式.jpg

1.4、优缺点

优点:

  • 将对象的创建交给专门的工厂类负责,实现了对象的创建和对象的使用分离

缺点:

  • 工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,违反了开闭原则

2、工厂方法模式

2.1、定义

工厂方法模式的定义为:定义一个用于创建对象的接口或抽象类,让子类决定实例化哪一个类。

2.2、UML 图

03 工厂模式.jpg

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();

    }
}

运行结果:

04 工厂模式.jpg

2.4、优缺点

优点:

  • 遵循了开闭原则,扩展性极强。比如现在要增加一个绿皮肤的人类,我们只需要增加一个创建绿皮肤人类的工厂,这个工厂继承自抽象工厂即可,不需要改变原有代码,可维护性高

缺点:

  • 增加了类的数量,当有成千上万个类型的产品时,就需要有成千上万个工厂类来生产这些产品。使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖

3、抽象工厂模式

3.1、定义

抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象。

不过,乍一听,我们可能有点懵,所以我们先来了解一下产品族与产品等级

产品族:

一个人种下面的所有性别人;例如白种人下面的男人,女人 称为白种人的产品族

产品等级:

多个人种下面的同性别人;例如白种人和黑种人下面的男人,称为一个产品等级

06 工厂模式.jpg


类图分析:

以下图为例,有男人和女人两种性别人,有白种人和黑种人两种人种,两种人种都可以由女娲捏出男人和女人。

  1. 有男人和女人两种性别人,定义两个抽象类接口
  2. 白种人和黑种人都有这两种性别人,所以有 4 个实现类
  3. 现在需要创建白种人和黑种人的工厂类,先将工厂类进行抽象成抽象类,里面有创建两个性别人的方法,返回的是性别人的抽象类
  4. 创建白种人和黑种人的工厂实现类,继承工厂类接口,实现创建各自性别人的方法
  5. 客户端调用时,直接用工厂接口类创建需要的工厂,拿到对应的性别人

07 工厂模式.jpg

3.2、UML 图

05 工厂模式.jpg

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();

    }
}

运行结果:

08 工厂模式.jpg

3.4、优缺点

优点:

  • 增加固定类型产品的不同具体工厂比较方便。例如现在女娲要创造一个绿皮肤的人,只需要再创建一个绿皮肤人的工厂继承抽象工厂就可以了
  • 一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象(将一个系列的产品统一一起创建)

缺点:

  • 类图有点复杂,可读性没有工厂方法模式那么好
  • 增加了系统的抽象性和理解难度

3.5、优缺举例:新增一个产品族或产品等级会怎样?

拓展一个产品族

我们会发现,拓展一个产品族是非常困难的,例如产品族中新增一个中性人,也就是说白种人和黑种人现在可以生产中性人了,如下图所示(红色框和文本为新增一个产品族需要做的事),对顶层的工厂接口类也要修改,这是非常麻烦的

09 工厂模式.jpg

拓展一个产品等级

如果扩展一个产品等级,例如新增一个男人,也就是说新增一个人种来生产男人,如下图所示(红色框为新增一个产品等级需要做的事),新增一个产品等级不用修改原来的代码,符合 OCP 原则,这是非常舒服的

10 工厂模式.jpg

三、总结

以上就是我个人关于 设计模式——工厂模式 的一些笔记,如果有什么问题,可以将问题发我邮箱 luodiab@126.com,欢迎各位的意见。

四、参考文章

设计模式篇——工厂模式详解

抽象工厂模式(通俗易懂)

  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    198 引用 • 120 回帖
1 操作
luodiab 在 2023-08-08 23:51:19 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...