工厂方法模式

140 阅读6分钟

工厂方法模式

本篇工厂方法模式,借鉴参考《大话设计模式》中的例子,使用了 Java 来进行简单的实现。

回顾一下简单工厂模式

简单工厂模式的实现

结构图

---
title: 计算器程序结构图(简单工厂模式)
---
classDiagram
    direction LR
    Operation <|-- Addition
    Operation <|-- Subtraction
    Operation <|-- Multiplication
    Operation <|-- Division

    Operation <.. OperationFactory

    class OperationFactory {
        + createOperation() Operation
    }

    class Operation {
        + double NumberA
        + double NumberB
        + GetResult() double
    }

    class Addition {
        + GetResult() double
    }

    class Subtraction {
        + GetResult() double
    }

    class Multiplication {
        + GetResult() double
    }

    class Division {
        + GetResult() double
    }  

工厂类

public class OperationFactory {
    public static Operation createOperator(String operator) throws Exception {
        Operation operation;

        switch (operator) {
            case "+":
                operation = new Addition();
                break;
            case "-":
                operation = new Subtraction();
                break;
            case "*":
                operation = new Multiplication();
                break;
            case "/":
                operation = new Division();
                break;
            default:
                throw new Exception("运算符输入错误");
        }
        return operation;
    }
}

客户端

Operation operation;
        operation=OperationFactory.createOperator(operator);
        operation.setNumberA(Double.valueOf(numberA));
        operation.setNumberB(Double.valueOf(numberB));
        Double result=operation.getResult();

工厂方法模式

结构图

---
title: 计算机程序结构图(工厂方法模式)
---
classDiagram
    direction BT
    加法类 --|> 运算类
    减法类 --|> 运算类
    乘法类 --|> 运算类
    除法类 --|> 运算类

    工厂类 --> 运算类
    加法工厂 --|> 工厂类
    减法工厂 --|> 工厂类
    乘法工厂 --|> 工厂类
    除法工厂 --|> 工厂类

    加法工厂 --> 加法类
    减法工厂 --> 减法类
    乘法工厂 --> 乘法类
    除法工厂 --> 除法类
    class 运算类 {
        + numberA: double
        + numberB: double
        + getResule(): double
    }
    class 加法类 {
        + getResule(): double
    }
    class 减法类 {
        + getResule(): double
    }
    class 乘法类 {
        + getResule(): double
    }
    class 除法类 {
        + getResule(): double
    }

    class 工厂类 {
        <<interface>>
        + createOperation(): 运算类
    }
    class 加法工厂 {

    }
    class 减法工厂 {

    }
    class 乘法工厂 {

    }
    class 除法工厂 {

    }

计算机程序的工厂方法结构图如上所示

先构建一个工厂接口

public interface IFactory {

    /**
     * 创建计算操作类
     *
     * @return {@link Operation}
     */
    Operation createOperation();
}

具体的运算方法

/**
 * 加法工厂
 */
public class AddFactory implements IFactory {
    @Override
    public Operation createOperation() {
        return new Addition();
    }
}

/**
 * 减法工厂
 */
public class SubFactory implements IFactory {
    @Override
    public Operation createOperation() {
        return new Subtraction();
    }
}

/**
 * 乘法工厂
 */
public class MulFactory implements IFactory {
    @Override
    public Operation createOperation() {
        return new Multiplication();
    }
}

/**
 * 除法工厂
 */
public class DivFactory implements IFactory {
    @Override
    public Operation createOperation() {
        return new Division();
    }
}

客户端

public class Calculator {

    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);

        System.out.print("请输入数字 A:");
        String numberA = scanner.nextLine();
        System.out.print("请输入运算符号(+、-、*、/):");
        String operator = scanner.nextLine();
        System.out.print("请输入数字 B:");
        String numberB = scanner.nextLine();

        IFactory iFactory;
        Operation operation;
        switch (operator) {
            case "+":
                iFactory = new AddFactory();
                operation = iFactory.createOperation();
                break;
            case "-":
                iFactory = new SubFactory();
                operation = iFactory.createOperation();
                break;
            case "*":
                iFactory = new MulFactory();
                operation = iFactory.createOperation();
                break;
            case "/":
                iFactory = new DivFactory();
                operation = iFactory.createOperation();
                break;
            default:
                throw new Exception("运算符输入错误");
        }
        
        operation.setNumberA(Double.valueOf(numberA));
        operation.setNumberB(Double.valueOf(numberB));
        Double result = operation.getResult();

        System.out.printf("计算结果:%s %s %s = %s %n", numberA, operator, numberB, result);
    }
}

参照简单工厂模式的实现进行对比。

简单工厂与工厂方法对比

在简单工厂模式的实现里面,我们要增加其他的运算,比如计算 A 的 N 次方、A 的 N 次方根,这些功能的增加,可以先去添加求 A 数的 N 次方功能类,然后再去更改工厂方法,在其中添加 case 语句来进行判断。现在再来看一下工厂方法的实现,添加功能类没问题,再加相关的工厂类也没问题,但是还要再去更改客户端,这样不等于不但没有简化难度,反而增加了很多类和方法,增加了复杂性么?为什么要这样呢?

**简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖 **。就像是使用计算器,客户端不需要去管该使用哪个类的实例,只需要把 + 给工厂,工厂就可以自动地给出相应的实例,客户端去调用运算方法进行运算取得结果就可以了,不同的实例会实现不同的运算。但是问题也是出现在这里,如果要新增一个 求 A 的 N 次方 的功能,就一定需要给运算工厂类的方法里面添加 case 的条件分支,这样会修改原有的类,这样会造成,不但对扩展开放了,对修改也开放了,违背了 开放-封闭原则,再看一下工厂方法。

工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

结构图

---
title: 工厂方法模式结构图
---
classDiagram
    direction RL
    ConcreteProduct --|> Product
    ConcreteCreator --> ConcreteProduct
    ConcreteCreator --|> Creator

    note for Product "定义工厂方法所创建的对象的接口"
    class Product {

    }

    note for ConcreteProduct "具体的产品,实现了 Product 接口"
    class ConcreteProduct {

    }

    note for Creator "声明工厂方法,该方法返回一个 Product 类型的对象"
    class Creator {
        + factoryMethod(): Product
    }

    note for ConcreteCreator "重定义工厂方法以返回一个 ConcreteProduct 实例"
    class ConcreteCreator {
        + factoryMethod(): Product
    }

既然工厂类和分支耦合,那么就可以对它下手,更具依赖倒转原则,可以将工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法。然后,所有的要生产具体类的工厂,就去实现这个接口,这样,一个简单工厂模式的工厂类,变成了一个工厂抽象接口和多个具体生成对象的工程,于是要增加 求 A 数的 N 次方 功能的时候,就不需要更改原有的工厂类了,只需要增加此功能的运算类和相应的工厂类就可以了。

这样,整个工厂和产品体系其实都没有修改的变化,而只是扩展的变化,这就完全复合了 开放-封闭 原则了。

再仔细观察一下计算器程序的工厂方法模式结构图,* *工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。想要新增功能,本来是修改工厂类,而现在是修改客户端了。 ** 这个可以利用 反射 来避免分支判断的问题,后续会补充到其他章节。

《大话设计模式》中还有一个雷锋的案例,但工厂方法到这里已经比较清晰了,此章不再补充这个案例了,感兴趣的可以阅读原书。

优缺点与适用场景

简单工厂模式和工厂方法模式都属于工厂模式的变体,它们的主要区别在于实现方式和适用场景。

  1. 简单工厂模式

简单工厂模式是一种创建型设计模式,它通过一个工厂类来创建不同类型的对象,这些对象具有相同的父类或接口。简单工厂模式的主要优点是简单易懂,适用于创建的对象数量较少,且对象的类型比较固定的情况。但是,它的缺点是当需要添加新类型的对象时,需要修改工厂类的代码,违反了 开放-封闭 原则。

适用场景:

  • 需要创建的对象数量少。

  • 对象类型比较固定,不会经常修改。

  • 可以接受工厂类的代码需要修改的缺点。

  1. 工厂方法模式

工厂方法模式也是一种创建型设计模式,它通过将对象的创建委托给子类来实现。每个子类都实现了工厂方法,用于创建特定类型的对象。工厂方法模式的主要优点是添加新类型的对象时,只需要添加相应的子类而无需修改原有的代码,符合 开发-封闭 原则。但是,相比于简单工厂模式,它的代码结构更加复杂。

适用场景:

  • 需要创建的对象数量较多。

  • 对象类型经常变化,需要灵活性。

  • 可以接受增加代码复杂度的缺点。

总的来说,如果对象类型比较固定,且需要创建的对象数量不多,可以使用简单工厂模式;如果对象类型比较多,经常变化,需要灵活性,可以使用工厂方法模式。