编程 Go中使用依赖注入的实用技巧

2024-11-19 00:24:20 +0800 CST views 496

Go 依赖注入:编写更好代码的实用技巧

提示 1:接口,接口,接口

第一个提示至关重要。结合后续提示,它将显著提高代码库的可维护性。

始终为每个具有函数的结构体创建一个接口。这很重要,原因如下:

  • 定义接口 封装了与结构体相关的行为。
  • 简化测试,可以使用像 GoMock 这样的库。
  • 如果代码中没有接口,就无法充分利用 Go 的特性,也无法使用 mock,这对有效测试至关重要。

示例

让我们创建一个具有两个函数的 SQL 仓库:CreateUserGetUserLastname

package main

import "database/sql"

type IUserRepository interface {
    CreateUser(firstname string, lastname string) error
    GetUserLastname(firstname string) (*string, error)
}

type userRepository struct {
    db *sql.DB
}

func (r *userRepository) CreateUser(firstname string, lastname string) error {
    // TODO: 与数据库交互,创建用户
    return nil
}

func (r *userRepository) GetUserLastname(firstname string) (*string, error) {
    // TODO: 与数据库交互,检索信息
    return nil, nil
}

func InitUserRepository(db *sql.DB) IUserRepository {
    return &userRepository{db: db}
}

这看起来很简单,但这是至关重要的。

从上到下:

  1. 定义了包含所有函数签名的接口,并且它是公开的(首字母大写)。
  2. 定义了结构体并将一些函数链接到它。结构体定义是私有的(小写),DB 实例也是。这确保了仓库只能在当前包内定义,DB 变量只能设置一次。
  3. 创建了一个初始化结构体的函数,该函数公开并返回接口类型。

遵循这种模式,强制开发人员调用 InitUserRepository 来初始化 userRepository,确保接口得到遵守。

提示 2:合并结构体和接口以提高可维护性

如果你的用户仓库实现了 20 或 30 个函数,文件和测试文件可能会变得庞大且难以管理。

在 Go 中,可以合并结构体和接口。这样可以为每个函数创建一个单独的文件。虽然文件数量会增加,但好处显著:

  • 减少认知负荷:处理小文件,专注于当前函数。
  • 简化代码审查:团队中的开发人员可以更快速地理解代码。
  • 测试文件 只包含特定函数的测试,更容易编写和维护。

实践示例

// file createUser.go
package main

import "database/sql"

type ICreateUser interface {}

type createUser struct {
    db *sql.DB
}

func (r *createUser) CreateUser(firstname string, lastname string) error {
    // TODO: 与数据库交互,创建用户
    return nil
}

func InitCreateUser(db *sql.DB) ICreateUser {
    return &createUser{db: db}
}

// file getUser.go
package main

import "database/sql"

type IGetUser interface {
    GetUserLastname(firstname string) (*string, error)
}

type getUserLastname struct {
    db *sql.DB
}

func (r *getUserLastname) GetUserLastname(firstname string) (*string, error) {
    // TODO: 与数据库交互,获取信息
    return nil, nil
}

func InitGetUserLastname(db *sql.DB) IGetUser {
    return &getUserLastname{db: db}
}

// file UserRepository.go
package main

import "database/sql"

type IUserRepository interface {
    ICreateUser
    IGetUser
}

type UserRepository struct {
    ICreateUser
    IGetUser
}

func InitUserRepository(db *sql.DB) IUserRepository {
    createUser := InitCreateUser(db)
    getUserLastname := InitGetUserLastname(db)
    return &UserRepository{
        ICreateUser: createUser,
        IGetUser:    getUserLastname,
    }
}

// main.go
package main

func main() {
    db := FAKE_init_db()
    
    userRepository := InitUserRepository(db)
    userRepository.GetUserLastname()
    userRepository.CreateUser()
}

提示 3:在依赖注入中只注入接口

依赖注入的概念可以被视为一个“套娃”类或结构体,每一层都依赖于下一层。

示例

仓库示例

// userRepository.go
package main

import "database/sql"

type IUserRepository interface {
    GetUserLastname(firstname string) (*string, error)
}

type UserRepository struct {
    db *sql.DB
}

func (r *UserRepository) GetUserLastname(firstname string) (*string, error) {
    // 实现逻辑
    return nil, nil
}

func InitUserRepository(db *sql.DB) IUserRepository {
    return &UserRepository{db: db}
}

服务示例

// userService.go
package main

type IUserService interface {
    GetUserLastname(firstname string) (*string, error)
}

type userService struct {
    repo IUserRepository
}

func (s *userService) GetUserLastname(firstname string) (*string, error) {
    return s.repo.GetUserLastname(firstname)
}

func InitUserService(repo IUserRepository) IUserService {
    return &userService{repo: repo}
}

处理程序示例

// userHandler.go
package main

type IUserHandler interface {
    GetUserLastnameHandler(firstname string) string
}

type UserHandler struct {
    service IUserService
}

func (h *UserHandler) GetUserLastnameHandler(firstname string) string {
    // 处理逻辑
    return ""
}

func InitUserHandler(service IUserService) IUserHandler {
    return &UserHandler{service: service}
}

主程序示例

// main.go
package main

func main() {
    config := GetConfig()

    db, err := InitDb(config.DbConfig)
    if err != nil {
        log.Fatal(err)
    }

    // 初始化依赖
    userRepository := InitUserRepository(db)
    userService := InitUserService(userRepository)
    userHandler := InitUserHandler(userService)

    // 启动应用
    app := InitApp(userHandler)
    app.Run()
}

结论

这些提示将显著改善你的 Go 开发工作流程,使你的代码更具可维护性、可测试性和灵活性。依赖注入是一种强大的工具,若有效使用,可以提升项目质量。如果你有其他提示或经验,请在评论中分享,我很乐意听到你的见解!别忘了订阅以获取我最新的文章更新。你的支持对我来说意义重大。

复制全文 生成海报 编程 Go语言 软件开发 设计模式

推荐文章

JavaScript 流程控制
2024-11-19 05:14:38 +0800 CST
liunx宝塔php7.3安装mongodb扩展
2024-11-17 11:56:14 +0800 CST
25个实用的JavaScript单行代码片段
2024-11-18 04:59:49 +0800 CST
前端开发中常用的设计模式
2024-11-19 07:38:07 +0800 CST
如何实现生产环境代码加密
2024-11-18 14:19:35 +0800 CST
全新 Nginx 在线管理平台
2024-11-19 04:18:33 +0800 CST
Vue 3 路由守卫详解与实战
2024-11-17 04:39:17 +0800 CST
Golang 中你应该知道的 noCopy 策略
2024-11-19 05:40:53 +0800 CST
如何在 Vue 3 中使用 Vuex 4?
2024-11-17 04:57:52 +0800 CST
一个简单的打字机效果的实现
2024-11-19 04:47:27 +0800 CST
js常用通用函数
2024-11-17 05:57:52 +0800 CST
如何使用go-redis库与Redis数据库
2024-11-17 04:52:02 +0800 CST
html一份退出酒场的告知书
2024-11-18 18:14:45 +0800 CST
介绍Vue3的Tree Shaking是什么?
2024-11-18 20:37:41 +0800 CST
20个超实用的CSS动画库
2024-11-18 07:23:12 +0800 CST
Nginx 性能优化有这篇就够了!
2024-11-19 01:57:41 +0800 CST
Python 微软邮箱 OAuth2 认证 Demo
2024-11-20 15:42:09 +0800 CST
html折叠登陆表单
2024-11-18 19:51:14 +0800 CST
PHP 8.4 中的新数组函数
2024-11-19 08:33:52 +0800 CST
页面不存在404
2024-11-19 02:13:01 +0800 CST
一个数字时钟的HTML
2024-11-19 07:46:53 +0800 CST
程序员茄子在线接单