为什么需要依赖注入

作为一名软件开发人员,如果我们希望使代码保持清洁和可维护性,我们将代码分割成不同的层次。

通常边界至少放置在基础设施和业务逻辑之间。当我们专门处理复杂的业务逻辑时,基础架构依赖于我们的业务逻辑是可取的,这样在更改基础架构时不会破坏我们的软件。

开发一个新的软件项目时的第一个决定是否选择一个结构来实现分层。 大多数情况下,我选择简洁的架构,但您有另一个很好的选择,例如 Domain Driven Design(领域驱动设计)。

独立于您选择的架构,我们必须粘贴来自不同模块以提供新功能,而这正是依赖注入所发挥的作用。

如何实现依赖注入

理解依赖注入,没有比示例更好的了。 想象一下,我们必须为我们想象中的项目实现用户注册,并且我们将用简约的架构来实现这一点。

img

我们可以想出一个像这样的实体:

package entity
//User结构体
type User struct {
    email string
    password string
    name string
    dateOfBirth string
}
//业务级别的验证
func Validate() error {
  ...
}

现在我们来创建一个用户注册的用例:

package usecase
import "entity"
func RegisterUser(user entity.User) {
    //验证和保存用户逻辑
}

看起来我们需要一些基础设施模块来存储用户。 我们还没有在基础设施级别实现任何 UserRepository,我们还遇到了另一个问题,通过简洁的架构原则,更高级别的层无法知道低级别层的存在。

如何来解决这个问题?

不需要触及基础设施中的任何部分,我们会对我们的用例说,有某种用户保护程序可以帮助我们的用户注册。

package usecase
import "entity"

type UserSaver interface {
    Save(user entity.User) error
}
//UserRegisterUseCase 存储它的依赖关系
type UserRegisterUseCase struct {
    userSaver UserSaver
}
func newUserRegisterUseCase(userSaver UserSaver) UserRegisterUseCase {
    return &{userSaver}
}
func (u UserRegisterUseCase) RegisterUser(user entity.User) error {
    user.Validate()
    err := u.userSaver.Save(user)
    if (err != nil) {
       return err
    }
}

看起来不错, 我们甚至没有涉及到基础架构层,我们将整个用例从实体输入到保存操作。 这是高层次的依赖倒置,这是 Go 的一个优势。

继续实现我们的 UserRepository…

package infrastructure
import "entity"
//UserRepository 管理用户持久化
type UserRepository struct {
    psqlClient psql.PsqlClient
}
func NewUserRepository(psqlClient psql.PsqlClient) UserRepository
{
	return &UserRepository{psqlClient}
}
//Save user
func Save(user entity.User) error {
 ...
}

func GetByEmail(email string) error {
 ...
}

让我们来看看如何操作依赖注入的

package main
import (
  "usecase"
  "infrastructure"
  "psqlClient"
)
func main() {
    psqlClient := psqlClient.NewPsqlClient()
    userRepository := infrastructure.NewUserRepository(psqlClient)
    useCase := usecase.newUserRegisterUseCase(userRepository)
    //现在我们可以将用例注入到web或者cli
}

我们实例化并注入主模块中的每个依赖项。 通过这个我们可以在程序的单个点上看到并控制依赖关系树。

Advantages in Dependency Injection

img

正如你所看到的,我们确保最高的 2 层,实体和用例不知道最底层的存在。 这是至关重要的,因为基础架构可以改变而不会影响业务逻辑。

第二个好处是我们将 psql 第三方包分离到一个模块中。 本模块将支持在第三方软件包的更新版本中所做的每一项更改。

最后但并非最不重要的一点,使用依赖注入可以让你测试你的模块而不需要测试它们的依赖关系

让我们来看看我们的User Repository的例子,为什么在测试 User Repository 时,您还想测试Psql Client?使用依赖注入可以模拟 Psql 客户端,并确保 User Repository 调用正确的 Psql 客户端方法。

在 GO 中保持简单

在我看来手动实例化就够了。 如果项目足够大,您可以在主包中创建不同的文件来执行依赖注入,然后从main.go中调用它们。

转载请注明: 转载自Ryan 是菜鸟 | LNMP 技术栈笔记

如果觉得本篇文章对您十分有益,何不 打赏一下

谢谢打赏

本文链接地址: Go 实现依赖注入

知识共享许可协议 本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可