简单工厂模式

206 阅读6分钟

简单工厂模式

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

使用面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果

mermaid 的类图在手机上显示不友好,推荐使用电脑进行阅读。也可以克隆项目到本地,在 idea 中安装 mermaid 插件进行阅读。

项目地址:design-patterns ,将会持续更新各种模式的应用,以及后续的实际案例应用。

普通实现

public class Calculator {

    public static void main(String[] args) {
        /*
          实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果
         */
        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();

        String result = "";

        switch (operator) {
            case "+":
                result = String.valueOf(Double.parseDouble(numberA) + Double.parseDouble(numberB));
                break;
            case "-":
                result = String.valueOf(Double.parseDouble(numberA) - Double.parseDouble(numberB));
                break;
            case "*":
                result = String.valueOf(Double.parseDouble(numberA) * Double.parseDouble(numberB));
                break;
            case "/":
                if ("0".equals(numberB)) {
                    System.out.println("除数不能为 0");
                    return;
                }
                result = String.valueOf(Double.parseDouble(numberA) / Double.parseDouble(numberB));
                break;
            default:
                System.out.println("运算符输入错误");
        }

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

以上是一个简单的计算机程序实现,在控制台输入两个数和运算符之后,得到计算结果,这种实现方式只能满足当前的需求,如果有新的需求,要求提供开平方等其他运算,就需要修改源代码了,可扩展性比较差,若之后需求逐渐增多,此处代码会更加难以维护。

改造简单工厂模式

改造思路

将计算器代码中,负责运算的部分抽离成一个类,然后将具体的运算分别放到运算类的子类中,这样无论是扩展多少运算方式,都只需要继承运算类,并重写运算方法即可,这样做的好处是对于之前的业务逻辑的影响几乎为 0,不用担心新增功能之后,之前的业务逻辑无法使用。

运算类

public class Operation {

    private Double numberA;

    private Double numberB;

    public Double getNumberA() {
        return numberA;
    }

    public void setNumberA(Double numberA) {
        this.numberA = numberA;
    }

    public Double getNumberB() {
        return numberB;
    }

    public void setNumberB(Double numberB) {
        this.numberB = numberB;
    }

    public Double getResult() throws Exception {
        return 0.0;
    }
}

我们在运算类中,定义了参与运算的两个属性 NumberANumberB,还有一个 getResult() 方法,这个方法用来给子类重写的。

加减乘除类

// 加法
public class Addition extends Operation {

    @Override
    public Double getResult() {
        return getNumberA() + getNumberB();
    }
}


// 减法
public class Subtraction extends Operation {

    @Override
    public Double getResult() {
        return getNumberA() - getNumberB();
    }
}


// 乘法
public class Multiplication extends Operation {

    @Override
    public Double getResult() {
        return getNumberA() * getNumberB();
    }
}


// 除法
public class Division extends Operation {

    @Override
    public Double getResult() throws Exception {
        if (getNumberB() == 0) {
            throw new Exception("除数不能为 0");
        }
        return getNumberA() / getNumberB();
    }
}

以上加减乘除法的类,分别继承了运算类,并重写了父类的 getResult() 方法,实现自己每个类中的业务逻辑,若想新增其他运算方法,只需要新增运算方法类,并继承运算类,重写父类的 getResult() 方法即可。(当然,还需要修改简单工厂类,添加创建实例的逻辑。)

简单运算工厂类

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;
    }
}

简单工厂类,定义了一个 createOprator(String operator) 方法,用来根据用户输入的运算符不同,创建对应的实例。

测试类

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

        Operation operation;
        operation = OperationFactory.createOperator(operator);
        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);

    }
}

对比不使用设计模式的实现,不需要知道内部是怎么运算的,只需要把需要计算的数字和运算符号传给简单工厂类,就可以获得运算结果。这样可以使模块之间的依赖关系更加松散。

结构图

---
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
    }

优缺点

优点

  1. 易于理解和实现:简单工厂模式的名称本身就揭示了它的工作方式,这使得初学者易于理解。此外,该模式相对简单,易于实现和维护。

  2. 提高代码的可重用性:简单工厂模式允许将一组相关或相互依赖的对象创建出来,这使得代码可以更易于维护和扩展。

  3. 支持模块间的松耦合:通过使用工厂模式,可以将模块之间的耦合度降低。客户端不直接创建对象,而是由工厂创建,这使得模块之间的依赖关系更加松散。

缺点

  1. 工厂方法模式可能会导致大量对象创建:当工厂方法模式的一组对象之间有紧密的依赖关系时,可能会导致创建大量对象,这可能会浪费内存和时间。

  2. 可能会增加代码的复杂度:虽然简单工厂模式易于理解和实现,但它的实现可能会增加代码的复杂度。特别是在需要创建大量相关或相互依赖的对象时,这可能会使代码变得难以理解和调试。

  3. 可能降低代码的可测试性:由于简单工厂模式需要创建一个抽象工厂类和多个具体工厂类,这可能会降低代码的可测试性。如果需要测试这些工厂类,则需要创建独立的测试用例,这可能会增加代码的复杂性。

适用场景

  1. 创建复杂对象:如果一个对象的创建过程比较复杂,包含多个步骤或依赖于其他对象,可以使用简单工厂模式将创建过程封装起来,简化客户端的代码。

  2. 隐藏对象创建细节:如果客户端需要创建多个不同的对象,可以使用简单工厂模式将对象创建的细节封装起来,使客户端无需关心对象创建的具体实现。

  3. 代码解耦:使用简单工厂模式可以将客户端代码与产品类的代码解耦,使得客户端代码更加灵活,可以轻松地切换不同的产品实现。

  4. 限制对象的创建:使用简单工厂模式可以限制对象的创建,确保只有工厂能够创建对象,避免客户端直接创建对象导致的错误。

  5. 提高代码复用性:如果多个对象的创建过程相似,可以使用简单工厂模式将创建过程封装起来,提高代码复用性。