面向初学者的 Rust 依赖注入 (Dependency Injection)

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么依赖注入在 Rust 中很重要?

\n\n

在编写 Rust 程序时,迟早会遇到一个模块(例如服务或控制器)需要使用另一个模块(数据库、API、日志记录器)功能的情况。最简单的方法是在使用它的代码内部直接创建所需的对象:

\n\n
struct UserService;\n\nimpl UserService {\n    fn get_user(&self, id: u32) -> String {\n        let db = Database::new(); // ❌ 紧耦合\n        db.query_user(id)\n    }\n}\n
\n\n

这种方法会创建紧耦合。代码变得难以测试、重用和修改。依赖注入 (DI) 是一种模式,其中依赖项从外部传递给对象,而不是在对象内部创建。Rust 作为一种拥有所有权系统和 trait 的语言,提供了无需框架即可实现 DI 的优雅方式。

\n\n

在本文中,我们将通过实际示例来讲解如何在 Rust 中注入依赖项:从简单的结构体传递到使用 trait 和智能指针。

\n\n

1. 基本方法:通过构造函数传递依赖项

\n\n

在 Rust 中注入依赖项的最简单方法是将其作为构造函数或函数的参数传递。让我们看一个使用日志记录器的例子。

\n\n

糟糕的例子(无 DI)

\n\n
struct ConsoleLogger;\n\nimpl ConsoleLogger {\n    fn log(&self, msg: &str) {\n        println!("[LOG]: {}", msg);\n    }\n}\n\nstruct OrderService {\n    logger: ConsoleLogger, // ❌ 紧耦合到具体类型\n}\n\nimpl OrderService {\n    fn new() -> Self {\n        Self {\n            logger: ConsoleLogger, // 在内部创建\n        }\n    }\n\n    fn place_order(&self, item: &str) {\n        self.logger.log(&format!("订单: {}", item));\n    }\n}\n
\n\n

好的例子(使用 DI)

\n\n
struct ConsoleLogger;\n\nimpl ConsoleLogger {\n    fn log(&self, msg: &str) {\n        println!("[LOG]: {}", msg);\n    }\n}\n\nstruct OrderService {\n    logger: ConsoleLogger, // 依赖项从外部传入\n}\n\nimpl OrderService {\n    // 构造函数接受依赖项\n    fn new(logger: ConsoleLogger) -> Self {\n        Self { logger }\n    }\n\n    fn place_order(&self, item: &str) {\n        self.logger.log(&format!("订单: {}", item));\n    }\n}\n\nfn main() {\n    let logger = ConsoleLogger;\n    let service = OrderService::new(logger);\n    service.place_order("Rust 书籍");\n}\n
\n\n

发生了什么变化?现在 OrderService 不再自己创建日志记录器。它接收一个现成的。这已经是 DI 了,但有一个问题:我们仍然绑定到了具体的类型 ConsoleLogger。为了使代码更灵活,我们需要使用 trait。

\n\n

2. 使用 trait 进行依赖项抽象

\n\n

Rust 中的 trait 类似于其他语言中的接口。它们允许定义依赖项必须遵循的契约。

\n\n

定义 Logger trait

\n\n
trait Logger {\n    fn log(&self, msg: &str);\n}\n\nstruct ConsoleLogger;\nimpl Logger for ConsoleLogger {\n    fn log(&self, msg: &str) {\n        println!("[控制台]: {}", msg);\n    }\n}\n\nstruct FileLogger {\n    filename: String,\n}\nimpl Logger for FileLogger {\n    fn log(&self, msg: &str) {\n        // 模拟写入文件\n        println!("[文件 {}]: {}", self.filename, msg);\n    }\n}\n
\n\n

通过泛型注入 trait

\n\n
struct OrderService<L: Logger> {\n    logger: L, // 泛型类型,实现了 Logger trait\n}\n\nimpl<L: Logger> OrderService<L> {\n    fn new(logger: L) -> Self {\n        Self { logger }\n    }\n\n    fn place_order(&self, item: &str) {\n        self.logger.log(&format!("订单: {}", item));\n    }\n}\n\nfn main() {\n    let console_logge                

博客

书籍推荐