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

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

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

推荐文章

一些高质量的Mac软件资源网站
2024-11-19 08:16:01 +0800 CST
php获取当前域名
2024-11-18 00:12:48 +0800 CST
虚拟DOM渲染器的内部机制
2024-11-19 06:49:23 +0800 CST
Nginx 如何防止 DDoS 攻击
2024-11-18 21:51:48 +0800 CST
纯CSS实现3D云动画效果
2024-11-18 18:48:05 +0800 CST
Vue 中如何处理父子组件通信?
2024-11-17 04:35:13 +0800 CST
Git 常用命令详解
2024-11-18 16:57:24 +0800 CST
# 解决 MySQL 经常断开重连的问题
2024-11-19 04:50:20 +0800 CST
CSS 特效与资源推荐
2024-11-19 00:43:31 +0800 CST
使用 node-ssh 实现自动化部署
2024-11-18 20:06:21 +0800 CST
如何在Vue3中定义一个组件?
2024-11-17 04:15:09 +0800 CST
js常用通用函数
2024-11-17 05:57:52 +0800 CST
15 个 JavaScript 性能优化技巧
2024-11-19 07:52:10 +0800 CST
LangChain快速上手
2025-03-09 22:30:10 +0800 CST
PHP 微信红包算法
2024-11-17 22:45:34 +0800 CST
Nginx 负载均衡
2024-11-19 10:03:14 +0800 CST
使用 Vue3 和 Axios 实现 CRUD 操作
2024-11-19 01:57:50 +0800 CST
使用xshell上传和下载文件
2024-11-18 12:55:11 +0800 CST
微信小程序热更新
2024-11-18 15:08:49 +0800 CST
程序员茄子在线接单