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 写法相比有什么不同
- 不依赖类继承和接口,trait 和关联函数替代 OOP 工厂接口。
- 动态分发使用
Box<dyn Trait>代替虚函数指针。 - 静态分发可以直接使用泛型,性能几乎零开销。
- 构造逻辑集中在函数或模块中,不需要创建独立工厂类。
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. 一个简单例子思路
场景: 创建不同类型的数据库连接
- 定义
Databasetrait - 实现
MySql和Postgres - 调用方只传入类型字符串或枚举,工厂函数返回对应实例
- 可用泛型或
Box<dyn Trait>管理返回对象
15. 总结一句话
工厂模式的本质是:封装对象创建逻辑,让调用方无需关心具体构造方式,从而实现类型解耦和灵活扩展。在 Rust 中,关联函数、trait +
Box<dyn Trait>或枚举 match 是最常用实现方式。