设计模式之简单工厂模式

问题背景

假设我们需要开发一个计算器应用,该应用能够根据用户输入的操作符(例如"+", “-”, “*”, “/”)和两个数值,计算并返回结果。当前的挑战是如何在不同的操作符之间灵活切换,同时保持代码的简洁性和可维护性。此外,程序应该易于扩展,比如未来可能需要添加更复杂的数学操作。

对问题进行分析

在计算器应用中,用户输入的操作符需要对应到特定的计算操作。使用简单工厂模式可以优雅地解决这个问题,它允许我们根据输入的操作符动态创建对应的计算对象。这种设计模式包含以下三个主要组成部分:

  1. 产品接口:定义了所有在不同类型的计算器类中共同遵循的接口,比如一个计算方法。
  2. 具体产品类:实现产品接口的类,每一个类对应一种具体的数学操作(加法、减法、乘法、除法等)。
  3. 工厂类:一个决定何时创建哪个具体产品类实例的类。根据用户输入的操作符,工厂类实例化相应的产品类对象。

简单工厂模式的核心优势在于:

  • 封装性:用户只需要知道他们需要什么类型的计算操作,而不需要了解对象创建的细节。
  • 扩展性:增加新的计算类型时,只需添加一个新的具体产品类并修改工厂类即可,不需要修改现有的客户端代码。
  • 解耦:工厂类将产品类的实例化过程与使用这些对象的客户端代码分离,增强了系统的管理能力和低耦合性。

代码部分

1. 产品接口

首先,我们定义一个抽象基类Operation,它包含一个纯虚函数calculate,用于执行实际的计算操作。

class Operation {
public:
    virtual double calculate(double a, double b) = 0;
    virtual ~Operation() {}
};

2. 具体产品类

接下来,为每种数学操作定义一个具体的类。

class Add : public Operation {
public:
    double calculate(double a, double b) override {
        return a + b;
    }
};

class Subtract : public Operation {
public:
    double calculate(double a, double b) override {
        return a - b;
    }
};

class Multiply : public Operation {
public:
    double calculate(double a, double b) override {
        return a * b;
    }
};

class Divide : public Operation {
public:
    double calculate(double a, double b) override {
        if (b != 0) return a / b;
        else throw std::runtime_error("Division by zero.");
    }
};

3. 工厂类

定义一个工厂类,根据用户输入的操作符,创建相应的计算器对象。

class OperationFactory {
public:
    static Operation* createOperate(char operate) {
        switch (operate) {
            case '+': return new Add();
            case '-': return new Subtract();
            case '*': return new Multiply();
            case '/': return new Divide();
            default: throw std::invalid_argument("Invalid operator.");
        }
    }
};

4. 主函数

最后,我们实现main函数,用于接收用户的输入并使用工厂类创建对象,然后输出计算结果。

#include <iostream>

int main() {
    double num1, num2;
    char op;
    std::cout << "Enter first number, operator, and second number: ";
    std::cin >> num1 >> op >> num2;

    try {
        Operation* operation = OperationFactory::createOperate(op);
        double result = operation->calculate(num1, num2);
        std::cout << "Result: " << result << std::endl;
        delete operation;
    } catch (const std::exception& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }

    return 0;
}

代码分析

  1. 产品接口和具体产品类

    • 我们定义了一个Operation接口,它包含了一个纯虚函数calculate。这样做确保了所有的具体产品类(如Add, Subtract, Multiply, Divide)都实现了这个函数,即每个类都提供了执行特定数学操作的具体实现。
    • 通过继承Operation,每个具体的操作类都遵循了相同的接口,这使得工厂类可以统一地处理所有操作类型的对象。
  2. 工厂类

    • OperationFactory是一个工厂类,它的createOperate静态方法根据传入的操作符参数创建并返回一个适当的Operation对象。这种方法简化了对象的创建过程,并将对象创建的具体逻辑从客户端代码中解耦出来。
    • 使用switch语句来选择合适的操作类型简化了代码并增强了其可读性。此外,通过在默认情况下抛出异常,增强了程序的健壮性。
  3. 主函数

    • 主函数提供了与用户的交互界面,让用户输入两个数值和一个操作符。然后,它使用工厂类创建一个相应的Operation对象,并调用其calculate方法来获取结果。
    • 错误处理是通过捕获并响应异常来完成的,这确保了程序在面对非法输入时能够优雅地处理错误。

简单工厂模式的优缺点

优点:

  • 封装创建逻辑:工厂类封装了对象的创建,使得客户端代码不需要直接实例化对象,从而降低了代码的耦合性。
  • 易于扩展:增加新的操作类型时,只需要新增一个具体的产品类并在工厂类中添加相应的逻辑即可,无需修改现有的客户端代码。

缺点:

  • 工厂类的职责过重:当产品种类非常多时,工厂类可能会变得非常庞大,职责过重。
  • 不符合开闭原则:每次增加新的操作类型时,都需要修改工厂类,这违反了软件设计中的开闭原则(对扩展开放,对修改关闭)。

编程要点

简单工厂模式是一种设计模式,主要用于创建对象,而无需指定将被创建的具体类的确切类型。这个模式通过一个单独的工厂类来实现,该类决定创建哪个类型的对象,而客户端只需通过接口与生成的对象交互。这样做可以降低系统的复杂性,并帮助隔离具体类的实例化。下面是实现简单工厂模式的一些关键编程要点:

1. 定义产品接口

产品接口是所有具体产品类必须实现的接口。这个接口定义了所有产品应有的方法,确保所有具体产品类在功能上的一致性。

2. 创建具体产品类

每个具体产品类都继承自产品接口,并实现其方法。这些类代表了工厂方法将要创建的具体对象。

3. 实现工厂类

工厂类具有一个或多个方法,用于根据输入决定并创建具体产品类的实例。通常,这个决定是基于方法的参数。

4. 客户端代码

客户端不直接创建产品对象,而是通过工厂类来请求它们。这样,客户端与具体产品的创建过程解耦。

要点总结

  • 封装创建逻辑:工厂类封装了所有关于创建对象的逻辑,使得客户端不需要知道具体的产品类。
  • 易于扩展:添加新的产品类型时,只需扩展工厂方法,而不需要修改现有的客户端代码。
  • 低耦合性:客户端代码只依赖于产品接口,而不是具体的产品类,这降低了代码之间的依赖关系,提高了代码的可维护性。

结论

尽管简单工厂模式有其局限性,但它仍然是一个非常有用的设计模式,特别是在对象创建逻辑相对简单,对象种类数量不是非常庞大的情况下。在我们的计算器示例中,它有效地帮助我们管理不同的计算操作,使得代码更加清晰易维护。

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值