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

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

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语言 软件开发 设计模式

推荐文章

html一份退出酒场的告知书
2024-11-18 18:14:45 +0800 CST
Go 语言实现 API 限流的最佳实践
2024-11-19 01:51:21 +0800 CST
Vue3中如何进行错误处理?
2024-11-18 05:17:47 +0800 CST
Vue3中怎样处理组件引用?
2024-11-18 23:17:15 +0800 CST
前端如何优化资源加载
2024-11-18 13:35:45 +0800 CST
Vue3中的v-model指令有什么变化?
2024-11-18 20:00:17 +0800 CST
Vue3中的JSX有什么不同?
2024-11-18 16:18:49 +0800 CST
Vue3中如何处理组件的单元测试?
2024-11-18 15:00:45 +0800 CST
Vue3 vue-office 插件实现 Word 预览
2024-11-19 02:19:34 +0800 CST
html一个包含iPhoneX和MacBook模拟器
2024-11-19 08:03:47 +0800 CST
H5保险购买与投诉意见
2024-11-19 03:48:35 +0800 CST
使用Rust进行跨平台GUI开发
2024-11-18 20:51:20 +0800 CST
PHP 如何输出带微秒的时间
2024-11-18 01:58:41 +0800 CST
动态渐变背景
2024-11-19 01:49:50 +0800 CST
利用Python构建语音助手
2024-11-19 04:24:50 +0800 CST
Boost.Asio: 一个美轮美奂的C++库
2024-11-18 23:09:42 +0800 CST
用 Rust 构建一个 WebSocket 服务器
2024-11-19 10:08:22 +0800 CST
程序员茄子在线接单