Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

07_工厂模式 Factory Pattern

1. 基本信息

中文名称: 工厂模式

英文名称: Factory Pattern

模式类型: 创建型设计模式

Rust 中常见实现方式: 关联函数 (new / with_*)、trait + Box<dyn Trait>、泛型、枚举

工厂模式的核心是:将对象创建逻辑封装在独立的“工厂”里,调用方无需知道具体构造过程即可获取对象。

在 Rust 中,工厂模式不依赖类继承,而是通过 trait、关联函数和泛型实现创建解耦和动态选择。

代码示例


2. 模式核心思想

工厂模式的核心思想是:

将对象的创建过程封装起来,让调用方通过统一接口获得对象,而不直接依赖对象具体构造。

换句话说:

  • 调用方只关心“我要一个对象”,不关心“对象怎么生成”。
  • 工厂负责根据条件创建不同类型的对象或配置对象。

3. 这个模式解决什么问题

工厂模式主要解决以下问题:

对象构造逻辑复杂,调用方不想直接 new。
系统需要生成不同类型对象,但调用方只依赖统一接口。
对象类型可能变化,调用方不想频繁修改代码。
对象初始化需要集中管理(配置、缓存、预处理)。

例如:

  • 日志对象:控制台、文件、远程
  • 数据库连接:MySQL、PostgreSQL
  • 网络请求客户端:HTTP、HTTPS、WebSocket
  • GUI 控件:不同平台控件实现

工厂模式让创建逻辑和使用逻辑解耦,便于扩展和维护。


4. 不使用这个模式会怎样

如果不使用工厂模式,可能出现:

调用方直接调用构造函数,耦合具体类型。
修改对象类型或增加新类型时,需要修改调用方代码。
构造逻辑散落在多个地方,维护困难。
多处重复构造类似对象,导致冗余代码。

5. 传统面向对象中的实现思路

在 Java/C++ 等传统 OOP 语言中,工厂模式通常有以下角色:

工厂接口(Factory):声明创建方法。
具体工厂类(ConcreteFactory):实现创建方法。
产品接口(Product):声明对象抽象。
具体产品类(ConcreteProduct):实现具体对象。

调用方通过工厂接口获取产品对象,而不直接依赖具体构造。

例如:

Factory
 ├── createProduct() -> Product
ConcreteFactory
 └── createProduct() -> ConcreteProduct

6. Rust 中的实现思路

6.1 Rust 中通常怎么实现

Rust 没有类继承体系,因此工厂模式通常通过以下方式实现:

  • 关联函数(new / with_*)创建对象
#![allow(unused)]
fn main() {
struct MySqlConnection { url: String }

impl MySqlConnection {
    fn new(url: &str) -> Self {
        Self { url: url.to_string() }
    }
}
}
  • trait + Box<dyn Trait> 动态工厂
#![allow(unused)]
fn main() {
trait Database {
    fn connect(&self);
}

struct MySql;
struct Postgres;

impl Database for MySql {
    fn connect(&self) { println!("MySQL 连接"); }
}

impl Database for Postgres {
    fn connect(&self) { println!("Postgres 连接"); }
}

fn create_database(db_type: &str) -> Box<dyn Database> {
    match db_type {
        "mysql" => Box::new(MySql),
        "postgres" => Box::new(Postgres),
        _ => panic!("未知数据库类型"),
    }
}
}
  • 枚举 + match 创建对象(小型工厂)
#![allow(unused)]
fn main() {
enum DatabaseType { MySql, Postgres }

fn create_database2(db_type: DatabaseType) -> Box<dyn Database> {
    match db_type {
        DatabaseType::MySql => Box::new(MySql),
        DatabaseType::Postgres => Box::new(Postgres),
    }
}
}

6.2 和传统 OOP 写法相比有什么不同

  1. 不依赖类继承和接口,trait 和关联函数替代 OOP 工厂接口。
  2. 动态分发使用 Box<dyn Trait> 代替虚函数指针。
  3. 静态分发可以直接使用泛型,性能几乎零开销。
  4. 构造逻辑集中在函数或模块中,不需要创建独立工厂类。

6.3 Rust 中是否有更自然的替代写法

  • 对于简单对象,直接用 struct::new()struct { .. } 即可,无需专门工厂函数。
  • 对于配置复杂的对象,可用 构建者模式(Builder)结合工厂函数生成对象。
  • 对于需要动态选择类型的对象,使用 trait + Box<dyn Trait> 是最自然方式。

7. Rust 中涉及的语言特性

struct / impl
trait
Box<dyn Trait>
泛型
match / enum
所有权与借用

8. 性能与工程代价

  • 静态工厂(泛型 + new)几乎无开销。
  • 动态工厂(Box<dyn Trait>)需要一次虚函数调用,轻微性能开销。
  • 使用枚举 + match 创建对象性能接近静态工厂,且类型安全。
  • Rust 不需要手动管理内存或构造对象,避免了 OOP 中可能出现的对象泄漏问题。

9. 典型应用场景

数据库客户端创建(MySQL / PostgreSQL / SQLite)
网络客户端创建(HTTP / HTTPS / WebSocket)
日志系统创建(Console / File / Remote)
GUI 控件工厂(不同平台实现)
游戏角色或道具实例化

10. 和相似模式的区别

  • 单例模式:关注全局唯一实例,而工厂模式可以创建任意数量对象。
  • 原型模式:通过复制已有对象创建新对象,而工厂模式是通过封装构造逻辑创建对象。
  • 构建者模式:分步骤创建复杂对象,工厂模式通常一次性创建。
  • 享元模式:共享对象以节省内存,而工厂模式关注对象构造解耦,数量不限。

11. 使用该模式的优点

调用方无需关心对象构造细节
对象创建逻辑集中,便于维护
可以灵活选择对象类型
扩展性好,增加新类型无需修改调用方

12. 使用该模式的代价

动态分发可能带来轻微性能开销
工厂函数或模块过多可能增加代码复杂度
对简单对象可能显得冗余

13. 什么时候不应该使用

对象创建逻辑简单,直接 new 即可
不需要动态选择对象类型
工厂模式可能增加不必要抽象

14. 一个简单例子思路

场景: 创建不同类型的数据库连接

  • 定义 Database trait
  • 实现 MySqlPostgres
  • 调用方只传入类型字符串或枚举,工厂函数返回对应实例
  • 可用泛型或 Box<dyn Trait> 管理返回对象

15. 总结一句话

工厂模式的本质是:封装对象创建逻辑,让调用方无需关心具体构造方式,从而实现类型解耦和灵活扩展。在 Rust 中,关联函数、trait + Box<dyn Trait> 或枚举 match 是最常用实现方式。