From bf5a71a6bdb6bdd222843203a267992bd729a652 Mon Sep 17 00:00:00 2001 From: wangzipai <38389763+wangzipai@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:03:40 +0800 Subject: [PATCH] [PUBLISHER] Merge #2 --- content/Obsidian/编程模型及方法/依赖注入.md | 113 ++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 content/Obsidian/编程模型及方法/依赖注入.md diff --git a/content/Obsidian/编程模型及方法/依赖注入.md b/content/Obsidian/编程模型及方法/依赖注入.md new file mode 100644 index 000000000..6ee33190a --- /dev/null +++ b/content/Obsidian/编程模型及方法/依赖注入.md @@ -0,0 +1,113 @@ +--- +date created: 2024-10-29 11:28 +date updated: 2024-10-29 11:40 +tags: + - 设计模式 +share: "true" +--- + +# 概述 + +## 什么是依赖注入 + +**依赖注入**的核心思想是==将对象的依赖关系从类内部移到外部管理==。也就是说,**不是由类自己来创建它所依赖的对象,而是将这些依赖通过构造函数参数、方法参数或属性设置的方式传递给它**。这样,类不再负责依赖的创建和管理,而是依赖于外部注入,这样可以更方便地替换或修改依赖对象。 + +## 控制反转(Inversion of Control, IoC) + +依赖注入是==控制反转(IoC)的一种实现方式==。IoC 是一种设计原则,指的是将对象的控制权从内部转移到外部。依赖注入通过构造函数、方法或属性注入的方式,实现了 IoC。 + +## 依赖注入的好处 + +- **低耦合度**:减少对象之间的直接依赖,提高代码的灵活性。 +- **易于测试**:可以方便地替换依赖对象,进行单元测试。 +- **高可维护性**:通过集中管理依赖,便于维护和扩展。 + +## 依赖注入的类型 + +依赖注入主要有以下几种方式: + +- **构造函数注入(Constructor Injection)**:通过构造函数传递依赖。 + +- **方法注入(Method Injection)**:通过方法参数传递依赖。 + +- **属性注入(Property Injection)**:通过设置结构体的字段来传递依赖。 + +# 依赖注入解决了什么问题 + +## 硬编码的依赖 + +如果 `UserService` 自己创建了 `UserRepository`,就像这样: + +```go +type UserService struct { + repo *DatabaseUserRepository +} + +func NewUserService() *UserService { + return &UserService{ + repo: &DatabaseUserRepository{}, // 硬编码依赖 + } +} +``` + +这里的问题是:`UserService`**紧密依赖于**`DatabaseUserRepository`。如果你想要使用不同的 `UserRepository` 实现(例如,改成 `InMemoryUserRepository` 进行测试),就不得不修改 `UserService` 结构体中 `repo` 字段类型了。这种耦合降低了代码的灵活性和可测试性。 + +## 依赖注入的本质 + +依赖注入的精髓是将**对象所依赖的其他对象**从**类的内部**移到**类的外部**,并由外部来管理这些依赖。换句话说,**对象不再负责创建它的依赖,而是由外部传入这些依赖**。 + +```c +type UserService struct { + repo UserRepository // 依赖于接口,而不是具体实现 +} + +func NewUserService(repo UserRepository) *UserService { + return &UserService{repo: repo} // 依赖由外部传入 +} +``` + +通过这种方式: + +- `UserService` 不再依赖于特定的 `UserRepository` 实现,它只依赖于 `UserRepository` 接口。 +- 依赖的具体实现(如 `DatabaseUserRepository` 或 `InMemoryUserRepository`)可以在运行时由外部决定,并通过构造函数传入。这就是**依赖注入**。 + +# C语言中的依赖注入 + +C语言本身并不直接支持依赖注入模式,因为它没有内置的面向对象编程特性,如C++或Java中的类和接口。然而,可以==通过结构体和函数指针来模拟依赖注入的概念==。 + +以下是一个简单的依赖注入示例,使用结构体和函数指针来定义依赖和提供注入点: + +```c +#include +#include + +// 定义依赖接口 +typedef struct { + void (*print_message)(const char *message); +} Dependency; + +// 依赖的实现 +void print_message(const char *message) { + printf("%s\n", message); +} + +// 使用依赖的函数 +void use_dependency(Dependency *dependency) { + dependency->print_message("Hello, Dependency!"); +} + +int main() { + // 创建依赖实例 + Dependency dependency; + dependency.print_message = print_message; + + // 使用依赖 + use_dependency(&dependency); + + return 0; +} +``` + +在这个例子中,`Dependency` 结构体定义了一个打印消息的==函数指针==。`print_message` 函数实现了这个接口。`use_dependency` 函数接受一个`Dependency`结构体指针作为参数,并调用其`print_message`方法。在`main`函数中,我们创建了一个`Dependency`实例并设置了它的`print_message`方法指针指向`print_message`函数的实现。 + +这个例子展示了如何在不修改`use_dependency`函数代码的情况下,通过结构体和函数指针来动态注入依赖。虽然这不是面向对象编程中的依赖注入,但在C语言中可以通过这种方式模拟依赖注入。