quartz/content/Obsidian/编程模型及方法/依赖注入.md
2024-10-29 16:03:40 +08:00

4.4 KiB
Raw Blame History

date created date updated tags share
2024-10-29 11:28 2024-10-29 11:40
设计模式
true

概述

什么是依赖注入

依赖注入的核心思想是==将对象的依赖关系从类内部移到外部管理==。也就是说,不是由类自己来创建它所依赖的对象,而是将这些依赖通过构造函数参数、方法参数或属性设置的方式传递给它。这样,类不再负责依赖的创建和管理,而是依赖于外部注入,这样可以更方便地替换或修改依赖对象。

控制反转Inversion of Control, IoC

依赖注入是==控制反转IoC的一种实现方式==。IoC 是一种设计原则,指的是将对象的控制权从内部转移到外部。依赖注入通过构造函数、方法或属性注入的方式,实现了 IoC。

依赖注入的好处

  • 低耦合度:减少对象之间的直接依赖,提高代码的灵活性。
  • 易于测试:可以方便地替换依赖对象,进行单元测试。
  • 高可维护性:通过集中管理依赖,便于维护和扩展。

依赖注入的类型

依赖注入主要有以下几种方式:

  • 构造函数注入Constructor Injection:通过构造函数传递依赖。

  • 方法注入Method Injection:通过方法参数传递依赖。

  • 属性注入Property Injection:通过设置结构体的字段来传递依赖。

依赖注入解决了什么问题

硬编码的依赖

如果 UserService 自己创建了 UserRepository,就像这样:

type UserService struct {
	repo *DatabaseUserRepository
}

func NewUserService() *UserService {
	return &UserService{
		repo: &DatabaseUserRepository{},  // 硬编码依赖
	}
}

这里的问题是:UserService紧密依赖于DatabaseUserRepository。如果你想要使用不同的 UserRepository 实现(例如,改成 InMemoryUserRepository 进行测试),就不得不修改 UserService 结构体中 repo 字段类型了。这种耦合降低了代码的灵活性和可测试性。

依赖注入的本质

依赖注入的精髓是将对象所依赖的其他对象类的内部移到类的外部,并由外部来管理这些依赖。换句话说,对象不再负责创建它的依赖,而是由外部传入这些依赖

type UserService struct {
    repo UserRepository  // 依赖于接口,而不是具体实现
}

func NewUserService(repo UserRepository) *UserService {
    return &UserService{repo: repo}  // 依赖由外部传入
}

通过这种方式:

  • UserService 不再依赖于特定的 UserRepository 实现,它只依赖于 UserRepository 接口。
  • 依赖的具体实现(如 DatabaseUserRepository 或 InMemoryUserRepository)可以在运行时由外部决定,并通过构造函数传入。这就是依赖注入

C语言中的依赖注入

C语言本身并不直接支持依赖注入模式因为它没有内置的面向对象编程特性如C++或Java中的类和接口。然而可以==通过结构体和函数指针来模拟依赖注入的概念==。

以下是一个简单的依赖注入示例,使用结构体和函数指针来定义依赖和提供注入点:

#include <stdio.h>
#include <stdlib.h>
 
// 定义依赖接口
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语言中可以通过这种方式模拟依赖注入。