首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【设计模式精解】一文爆肝简单工厂、工厂方法、抽象工厂模式,助你轻松理解工厂模式

【设计模式精解】一文爆肝简单工厂、工厂方法、抽象工厂模式,助你轻松理解工厂模式

作者头像
用户11598978
发布2025-12-18 20:09:22
发布2025-12-18 20:09:22
140
举报
文章被收录于专栏:码力up码力up

工厂模式是干啥的?

工厂模式的主要功能就是帮助我们实例化对象的。之所以名字中包含工厂模式四个字,是因为对象的实例化过程是通过工厂实现的,是用工厂代替new操作的。

这样做的好处是封装了对象的实例化细节,尤其是对于实例化较复杂的情况。会给你系统带来更大的可扩展性和尽量少的修改量。

工厂模式有三种:

  1. 简单工厂 :一个工厂只创建一个具体产品。
  2. 工厂方法 :一个工厂方法只创建一类具体产品。
  3. 抽象工厂 :一个工厂创建所有具体产品。

工厂模式之前,我们是怎么写代码的?

假设我们要实现一个简单的计算器,最粗糙的代码实现如下:

代码语言:javascript
复制
public class Program {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("请输入第一个值: ");
        Double value1 = scanner.nextDouble();
        System.out.print("请选择运算符号 (+, -, *, /): ");
        String operation = scanner.next(); // 不会读取换行符本身,只会读取换行符之前的内容。
        System.out.print("请输入第二个值: ");
        Double value2 = scanner.nextDouble();

        String result = "";
        switch (operation) {
            case "+":
                result = String.valueOf(value1 + value2);
                break;
            case "-":
                result = String.valueOf(value1 - value2);
                break;
            case "*":
                result = String.valueOf(value1 * value2);
                break;
            case "/":
                if (value2 == 0) {
                    throw new RuntimeException("除数不能为0");
                }
                result = String.valueOf(value1 / value2);
                break;
            default:
                System.out.println("无效的运算符号");
                return;
        }
        System.out.println("运算结果为" + result);
    }
}

为了实现各算法之间的解耦,做到面向对象,需要进行改进。代码实现如下:

代码语言:javascript
复制
// 计算类的基类
public abstract class Operation {

    private double value1 = 0;
    private double value2 = 0;

    public double getValue1() {
        return value1;
    }
    public void setValue1(double value1) {
        this.value1 = value1;
    }
    public double getValue2() {
        return value2;
    }
    public void setValue2(double value2) {
        this.value2 = value2;
    }
    protected abstract double getResult();
}

//加法
public class OperationAdd extends Operation {
    @Override
    protected double getResult() {
        return getValue1() + getValue2();
    }
}
//减法
public class OperationSub extends Operation {
    @Override
    protected double getResult() {
        return getValue1() - getValue2();
    }
}
//乘法
public class OperationMul extends Operation {
    @Override
    protected double getResult() {
        return getValue1() * getValue2();
    }
}
//除法
public class OperationDiv extends Operation {
    @Override
    protected double getResult() {
        if (getValue2() != 0) {
            return getValue1() / getValue2();
        }
        throw new IllegalArgumentException("除数不能为零");
    }
}

当我想要执行加法运算时,可以使用如下代码:

代码语言:javascript
复制
public class Main {
    public static void main(String[] args) {
        OperationAdd operationAdd = new OperationAdd();
        operationAdd.setValue1(99);
        operationAdd.setValue2(99);
        System.out.println(operationAdd.getResult());
    }
}

当我需要执行减法运算时,我就要创建一个OperationSub类。也就是说,我想要使用不同的运算的时候就要创建不同的类,并且要明确知道该类的名字。 那么这种重复的创建类的工作其实可以放到一个统一的工厂类中,于是就有了工厂模式........

简单工厂模式

代码实现

需要在原有类的基础上,添加工厂类:

代码语言:javascript
复制
public class OperationFactory {
    public static Operation createOperation(String operate){
        Operation oper = null;
        switch (operate){
            case "+":
                oper = new OperationAdd();
                break;
            case "-":
                oper = new OperationSub();
                break;
            case "*":
                oper = new OperationMul();
                break;
            case "/":
                oper = new OperationDiv();
                break;
            default:
                break;
        }
        return oper;
    }
}

有了工厂类之后,可以使用工厂创建对象:

代码语言:javascript
复制
public class Test {
    public static void main(String[] args) {
        Operation operation = OperationFactory.createOperation("/");
        operation.setValue1(12);
        operation.setValue2(2);
        System.out.println(operation.getResult());
    }
}

通过简单工厂模式,计算器的使用者不需要关心实现加法逻辑的那个类的具体名字,他只要知道该类对应的操作符"+"、"-"、"*"、"/"就可以了。

结构组成

Factory:工厂类。在java中它往往由 一个具体类实现。(OperationFactory)

Product:抽象产品。在java中由接口或者抽象类来实现。(Operation)

ConcreteProduct:具体产品。在java中由一个具体类实现。(OperationAdd\OperationSub等)

UML类图

通用类图表示如下:

本例类图表示如下:

存在的问题

当我们需要增加一种计算时,例如开平方。这个时候我们需要先定义一个类继承Operation类,其中实现开平方的代码。除此之外我们还要修改OperationFactory类的代码,增加一个case。这显然是违背开闭原则的。可想而知对于新产品的加入,工厂类是很被动的。

工厂方法模式

代码实现

这里还用计算器的例子。在保持Operation,OperationAdd,OperationDiv,OperationSub,OperationMul等几个方法不变的情况下,修改简单工厂模式中的工厂类(OperationFactory)。替代原有的那个"万能"的大工厂类,这里使用工厂方法来代替:

代码语言:javascript
复制
//工厂接口
public interface IFactory {
    Operation CreateOption();
}

//加法类工厂
public class AddFactory implements IFactory {
    public Operation CreateOption() {
        return new OperationAdd();
    }
}

//除法类工厂
public class DivFactory implements IFactory {
    public Operation CreateOption() {
        return new OperationDiv();
    }
}

//乘法类工厂
public class MulFactory implements IFactory {
    public Operation CreateOption() {
        return new OperationMul();
    }
}

//减法类工厂
public class SubFactory implements IFactory {
    public Operation CreateOption() {
        return new OperationSub();
    }
}

这样,在客户端中想要执行加法运算时,需要以下方式:

代码语言:javascript
复制
public class Main {

    public static void main(String[] args) {
        IFactory factory = new AddFactory();
        Operation operationAdd =  factory.CreateOption();
        operationAdd.setValue1(1);
        operationAdd.setValue2(2);
        System.out.println(operationAdd.getResult());
    }
}

这样,当我们需要增加一种开平方的计算时,我们只需要新增一个开平方的产品类继承Operation类和一个实现了工厂接口的开平方工厂类就可以。

新增代码如下:

代码语言:javascript
复制
public class OperationSquare extends Operation {
    @Override
    protected double getResult() {
        if (getValue1() < 0) {
            throw new RuntimeException("不能对负数开平方");
        }
        return Math.sqrt(getValue1());
    }
}
代码语言:javascript
复制
public class SquareFactory implements IFactory{
    @Override
    public Operation createOperation() {
        return new OperationSquare();
    }
}

结构组成

Product:抽象产品(Operation)

ConcreteProduct:具体产品(OperationSquare)

Factory:抽象工厂(IFactory)

ConcreteFactory:具体工厂(SquareFactory)

UML类图

通用类图表示如下:

本例类图如下:

image.png
image.png

存在的问题

看到上图新增的SquareFactory类和OperationSquare类,每新增一个操作类就需要增加对应新的工厂,会发现需要花费很大的成本,现在才五个操作类,那么等添加更多计算功能到十个、一百个的时候就会变得更加的复杂和难以维护。

为什么要使用工厂来创建对象?

主要是为了封装对象的创建过程

工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心产品创建细节,甚至无须知道具体产品类的类名。

为什么每种对象要单独有一个工厂?

符合开闭原则

主要目的是为了解耦。在系统中加入新产品时,只需要添加一个具体工厂和具体产品就可以了,无须修改简单工厂中的大工厂的代码。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则。

抽象工厂模式

注意:本模式的例子参考了Bee.F的文章,并纠正了文章中的一些错误,详情也可查阅抽象工厂模式(通俗易懂)_抽象工厂 通俗的方式-CSDN博客

抽象工厂模式和工厂方法模式一样,都符合开放-封闭原则。但是不同的是,工厂方法模式在增加一个具体产品的时候,都要增加对应的工厂。但是抽象工厂模式只有在新增一个类型的具体产品时才需要新增工厂。也就是说,工厂方法模式的一个工厂只能创建一个具体产品。而抽象工厂模式的一个工厂可以创建属于一类类型的多种具体产品。工厂创建产品的个数介于简单工厂模式和工厂方法模式之间。

使用条件

系统中有多个产品族,每个具体工厂创建同一族但属于不同产品等级的产品。

系统一次只可能消费其中某一族产品,即同族的产品一起使用

产品族:一个品牌下面的所有产品;例如苹果下面的手机、电脑、平板称为苹果的产品族; 产品等级:多个品牌下面的同种产品;例如苹果和华为下面的手机称为一个产品等级;

结构组成

AbstractFactory(抽象工厂):用于声明生成抽象产品的方法

ConcreteFactory(具体工厂):实现了抽象工厂声明的生成抽象产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中;

AbstractProduct(抽象产品):为每种产品声明接口,在抽象产品中定义了产品的抽象业务方法;

Product(具体产品):定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。

类图分析

例子:有手机和路由器两种产品,小米和华为两种品牌,两种品牌都可以生成手机和路由器

  1. 定义手机和路由器两个接口;
  2. 华为和小米都可以生产这两种产品,所以有4个实现类;
  3. 现在需要创建工厂接口,里面有创建两个产品的方法,返回的是产品的接口类;
  4. 创建华为和小米的工厂实现类,实现工厂类接口,实现创建各自产品的方法;
  5. 客户端调用时,直接用工厂接口类创建需要的工厂,拿到对应的产品;

代码实现

定义手机和电脑两个接口

代码语言:javascript
复制
//手机产品接口
public interface IPhoneProduct {
    //开机
    void start();
    //关机
    void shutdown();
    //打电话
    void callup();
    //发邮件
    void sendSMS();
}

//路由器产品接口
public interface IRouterProduct {
    //开机
    void start();
    //关机
    void shutdown();
    //打开wifi
    void openwifi();
    //设置
    void setting();
}

华为的两个产品实现类+小米的两个产品实现类

代码语言:javascript
复制
// 华为手机实现类
public class HuaweiPhone implements PhoneProduct{
    @Override
    public void start() {
        System.out.println("华为手机开机");
    }

    @Override
    public void shutdown() {
        System.out.println("华为手机关机");
    }

    @Override
    public void callup() {
        System.out.println("华为手机打电话");
    }

    @Override
    public void sendSMS() {
        System.out.println("华为手机发短信");
    }
}

// 华为路由器实现类
public class HuaweiRouter implements RouterProduct{
    @Override
    public void start() {
        System.out.println("华为路由器启动了");
    }

    @Override
    public void shutdown() {
        System.out.println("华为路由器关机了");
    }

    @Override
    public void openwifi() {
        System.out.println("华为路由器打开wifi了");
    }

    @Override
    public void setting() {
        System.out.println("华为路由器设置中...");
    }
}

// 小米手机实现类
public class XiaomiPhone implements PhoneProduct{
    @Override
    public void start() {
        System.out.println("小米手机开机");
    }

    @Override
    public void shutdown() {
        System.out.println("小米手机关机");
    }

    @Override
    public void callup() {
        System.out.println("小米手机打电话");
    }

    @Override
    public void sendSMS() {
        System.out.println("小米手机发短信");
    }
}

// 小米路由器实现类
public class XiaomiRouter implements RouterProduct{
    @Override
    public void start() {
        System.out.println("小米路由器启动了");
    }

    @Override
    public void shutdown() {
        System.out.println("小米路由器关机了");
    }

    @Override
    public void openwifi() {
        System.out.println("小米路由器打开wifi了");
    }

    @Override
    public void setting() {
        System.out.println("小米路由器设置中...");
    }
}
  • 现在需要创建工厂接口,里面有创建两个产品的方法,返回的是产品的接口类;
代码语言:javascript
复制
//产品工厂接口
public interface ProductFactory {

    //生产手机
    PhoneProduct phoneProduct();

    //生成路由器
    RouterProduct routerProduct();
}
  • 创建华为和小米的工厂实现类,实现工厂类接口,实现创建各自产品的方法;
代码语言:javascript
复制
//华为工厂实现类
public class HuaweiFactory implements IProductFactory {

    @Override
    public IPhoneProduct phoneProduct() {
	return new HuaweiPhone();
    }

    @Override
    public IRouterProduct routerProduct() {
	return new HuaweiRouter();
    }
}

//小米工厂实现类
public class XiaomiFactory implements IProductFactory {

    @Override
    public IPhoneProduct phoneProduct() {
    	return new XiaomiPhone();
    }

    @Override
    public IRouterProduct routerProduct() {
	return new XiaomiRouter();
    }
}

客户端调用时,直接用工厂接口类创建需要的工厂,拿到对应的产品;

代码语言:javascript
复制
public class Client {
    public static void main(String[] args) {
        System.out.println("----------------小米产品--------------------");
        // 创建小米工厂
        ProductFactory xiaomiFactory = new XiaomiFactory();
        // 生产小米手机
        PhoneProduct xiaomiPhone = xiaomiFactory.phoneProduct();
        xiaomiPhone.start();
        xiaomiPhone.sendSMS();
        // 生产小米路由器
        RouterProduct xiaomiRouter = xiaomiFactory.routerProduct();
        xiaomiRouter.start();
        xiaomiRouter.openwifi();

        System.out.println("----------------华为产品--------------------");
        // 创建华为工厂
        HuaweiFactory huaweiFactory = new HuaweiFactory();
        // 生产华为手机
        PhoneProduct huaweiPhone = huaweiFactory.phoneProduct();
        huaweiPhone.start();
        huaweiPhone.sendSMS();
        // 生产华为路由器
        RouterProduct huaweiRouter = huaweiFactory.routerProduct();
        huaweiRouter.start();
        huaweiRouter.openwifi();
    }
}

运行结果

存在的问题

如果新增一个产品族或产品等级会怎样?

新增一个产品等级

观察下图我们会发现,拓展一个产品等级是非常困难的,例如产品等级中新增一个笔记本电脑,也就是说华为和小米现在可以生产电脑了,如下图所示(黄色字体为新增一个产品等级需要做的事),对顶层的工厂接口类也要修改,这是非常麻烦的;

新增一个产品族

如果扩展一个产品族,也就是说新增一个品牌来生产手机和路由器,如下图所示(黄色字体为新增一个产品族需要做的事),新增一个产品族不用修改原来的代码,符合OCP原则,这是非常舒服的;

结论:抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,但不能为新的产品等级结构的增加提供这样的方便。这种性质称为“开闭原则”的倾斜性。

最后

看到这你也许会困惑:既然每个模式都有问题,那到底该选哪个? 其实答案很简单——世上没有完美的事物,一切都有两面性。关键在于找到最适合你项目的方案,那就是最佳选择!

如果我的内容对你有帮助,请辛苦动动您的手指为我点赞,评论,收藏。感谢大家!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 工厂模式是干啥的?
  • 工厂模式之前,我们是怎么写代码的?
  • 简单工厂模式
    • 代码实现
    • 结构组成
    • UML类图
  • 工厂方法模式
    • 代码实现
    • 结构组成
    • UML类图
    • 存在的问题
    • 为什么要使用工厂来创建对象?
    • 为什么每种对象要单独有一个工厂?
  • 抽象工厂模式
    • 使用条件
    • 结构组成
    • 类图分析
    • 代码实现
    • 存在的问题
      • 新增一个产品等级
      • 新增一个产品族
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档