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

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

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

推荐文章

使用 sync.Pool 优化 Go 程序性能
2024-11-19 05:56:51 +0800 CST
Go 如何做好缓存
2024-11-18 13:33:37 +0800 CST
一些高质量的Mac软件资源网站
2024-11-19 08:16:01 +0800 CST
js一键生成随机颜色:randomColor
2024-11-18 10:13:44 +0800 CST
Vue3结合Driver.js实现新手指引功能
2024-11-19 08:46:50 +0800 CST
JavaScript数组 splice
2024-11-18 20:46:19 +0800 CST
Web 端 Office 文件预览工具库
2024-11-18 22:19:16 +0800 CST
Vue3中如何实现响应式数据?
2024-11-18 10:15:48 +0800 CST
18个实用的 JavaScript 函数
2024-11-17 18:10:35 +0800 CST
CSS 奇技淫巧
2024-11-19 08:34:21 +0800 CST
pycm:一个强大的混淆矩阵库
2024-11-18 16:17:54 +0800 CST
CSS 媒体查询
2024-11-18 13:42:46 +0800 CST
在 Rust 生产项目中存储数据
2024-11-19 02:35:11 +0800 CST
Git 常用命令详解
2024-11-18 16:57:24 +0800 CST
Vue3中哪些API被废弃了?
2024-11-17 04:17:22 +0800 CST
Go 开发中的热加载指南
2024-11-18 23:01:27 +0800 CST
PHP 的生成器,用过的都说好!
2024-11-18 04:43:02 +0800 CST
程序员茄子在线接单