C++工厂模式详解(附带实例)
工厂方法模式是一种非常实用的设计模式,尽管它的名字听起来可能有些抽象。其核心思想是定义一个用于创建对象的接口,让子类决定实例化哪个类。这使得类的实例化延迟到其子类。
工厂方法模式在开发中的应用非常广泛,因为它帮助解耦了对象的创建和使用,使得系统更易于扩展和维护。
在深入讨论工厂方法模式之前,有必要先提到简单工厂。简单工厂虽然不是正式的设计模式,但它是一种常用的对象创建实践。简单工厂通过一个独立的类来封装对象的创建过程,这样当在需要新对象时,代码不再直接实例化类,而是通过工厂类获取。尽管简单工厂简单有效,但它在应对类的增加时往往显得不足,因为每次添加新类都需要修改工厂类,这违反了开闭原则。
与简单工厂相比,工厂方法模式提供了一种更加灵活的方式来扩展产品类,因为它允许类在不修改现有代码的情况下引入新的类型。这可以通过定义一个用于创建对象的接口,并让子类实现该接口以确定实例化哪个特定类来实现,从而完成扩展。
工厂方法模式体现了“依赖倒置原则”,即:
在这种情境下,高层模块是调用工厂的代码,低层模块是具体的产品实现类,而抽象则是工厂和产品的接口。工厂方法模式如下图所示:

图 1 工厂方法模式
在工厂方法模式中,核心角色包括:
工厂方法模式在开发中的应用非常广泛,因为它帮助解耦了对象的创建和使用,使得系统更易于扩展和维护。
在深入讨论工厂方法模式之前,有必要先提到简单工厂。简单工厂虽然不是正式的设计模式,但它是一种常用的对象创建实践。简单工厂通过一个独立的类来封装对象的创建过程,这样当在需要新对象时,代码不再直接实例化类,而是通过工厂类获取。尽管简单工厂简单有效,但它在应对类的增加时往往显得不足,因为每次添加新类都需要修改工厂类,这违反了开闭原则。
与简单工厂相比,工厂方法模式提供了一种更加灵活的方式来扩展产品类,因为它允许类在不修改现有代码的情况下引入新的类型。这可以通过定义一个用于创建对象的接口,并让子类实现该接口以确定实例化哪个特定类来实现,从而完成扩展。
工厂方法的思想
在工厂方法模式中,创建对象的任务被转移到实现了工厂接口的具体子类中,从而使得添加新产品类时,系统无须修改已有代码。这种模式特别有助于创建复杂对象,其创建过程需要大量配置选项或依赖不同条件。工厂方法模式体现了“依赖倒置原则”,即:
- 高层模块不应依赖低层模块,两者都应依赖抽象;
- 抽象不应依赖细节,细节应依赖抽象。
在这种情境下,高层模块是调用工厂的代码,低层模块是具体的产品实现类,而抽象则是工厂和产品的接口。工厂方法模式如下图所示:

图 1 工厂方法模式
在工厂方法模式中,核心角色包括:
- 抽象产品(Product):定义了产品对象的接口;
- 具体产品(Concrete Product):实现抽象产品接口的具体类;
- 抽象工厂(Creator):声明了工厂方法,该方法返回一个抽象产品;
- 具体工厂(Concrete Creator):重写工厂方法以返回一个具体产品实例。
工厂方法模式的应用场景
工厂方法模式主要应用在以下场景:- 产品族:当存在多个产品系列,且这些产品都设计为一起使用时,工厂方法可以确保客户端始终只使用同一个产品系列的对象;
- 依赖注入:在需要灵活地向应用程序注入不同行为或资源时,工厂方法可以用于创建这些对象的实例,增加程序的灵活性和可配置性;
- 可扩展性需求:当系统预计会频繁地添加新产品,或者需要根据不同的环境条件创建不同的对象时,使用工厂方法模式可以避免构造函数的泛滥,使得系统更容易管理。
工厂方法实例展示
考虑一个简单的日志记录系统,我们可能需要根据不同的运行环境(开发环境、生产环境)来决定是否将日志信息输出到控制台或者文件。class Logger { public: virtual void log(const std::string& message) = 0; }; class ConsoleLogger : public Logger { public: void log(const std::string& message) override { std::cout << "Console log: " << message << std::endl; } }; class FileLogger : public Logger { public: void log(const std::string& message) override { // 将消息写入文件的代码 } }; class LoggerFactory { public: virtual Logger* createLogger() = 0; }; class ConsoleLoggerFactory : public LoggerFactory { public: Logger* createLogger() override { return new ConsoleLogger(); } }; class FileLoggerFactory : public LoggerFactory { public: Logger* createLogger() override { return new FileLogger(); } };通过使用工厂方法模式,我们的代码不直接依赖于具体的日志类,而是依赖于一个抽象的 Logger 接口。这样,我们可以轻松地添加新的日志方法,或者更改日志记录的方式,而不需要修改依赖于日志的代码。