Golang依赖注入 - 应该使用值传递还是指针/引用传递?

Golang依赖注入 - 应该使用值传递还是指针/引用传递? 由于我没有找到与这个特定问题相关的太多资料,我直接在这里提问:

假设我们有一个包含存储、数据库连接和一些服务等的应用程序。由于我之前使用PHP,目前我通过指针注入这些依赖项,例如:

服务 <- 存储 <- 数据库连接

但这真的是在Go中应该采用的方式吗?还是通过值注入这些依赖项更为合适?

5 回复

我的观点是,"按值传递"意味着会创建一个副本。我认为这对于数据库连接或类似服务来说没有意义。

更多关于Golang依赖注入 - 应该使用值传递还是指针/引用传递?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我知道区别。但这与状态变化的对象无关——而是关于在应用程序启动时创建的对象图。

但我不知道对此是否有任何约定。

按值传递会创建一个副本参考以下示例

package main

import (
	"fmt"
)

type Service struct {
	counter int
}

func (service *Service) inc() {
	service.counter = service.counter + 1
}

func manipulate(service Service) Service {
	service.inc()
	return service
}

func main() {
	s := Service{}
	s.inc()
	s.inc()
	fmt.Println("s", s)

	t := manipulate(s)
	fmt.Println("s", s) // 仍然计数为2!
	fmt.Println("t", t) // 是副本且计数为3!
}

输出:

s { 2}
s { 2}
t { 3}

始终使用指针。

对于较小的数据结构,你可以按值传递,但对于较大的数据结构这样效率不高,因此应该通过引用传递。数据库连接肯定要作为引用传递。如果这与HTTP服务器相关,以下内容可能会很有趣:

文章缩略图

七年后我如何编写Go HTTP服务——Statuscode——Medium

我从r59版本就开始编写Go(书面写作时称为Golang)——那是1.0之前的版本

阅读时间:6分钟

在Go语言中,依赖注入通常应该使用指针传递,而不是值传递。这主要是出于性能和状态一致性的考虑。

为什么推荐指针传递

1. 避免不必要的拷贝

依赖项(如数据库连接、存储服务等)通常包含大量数据或资源,值传递会导致完整的结构体拷贝,造成性能开销。

type Database struct {
    conn *sql.DB
    config Config
    // 可能还有其他大量字段
}

type Storage struct {
    db *Database
    cache map[string]interface{}
}

type Service struct {
    storage *Storage
}

// 使用指针注入
func NewService(storage *Storage) *Service {
    return &Service{storage: storage}
}

2. 确保状态一致性

当多个组件共享同一个依赖时,使用指针可以确保它们操作的是同一个实例,状态变更对所有使用者可见。

type Config struct {
    Timeout int
}

type Database struct {
    config *Config
}

type ServiceA struct {
    db *Database
}

type ServiceB struct {
    db *Database
}

// 所有服务共享同一个配置实例
config := &Config{Timeout: 30}
db := &Database{config: config}
serviceA := &ServiceA{db: db}
serviceB := &ServiceB{db: db}

// 修改配置对所有服务生效
db.config.Timeout = 60

3. 支持接口实现

依赖注入通常结合接口使用,而接口值本身包含指向具体实现的指针。

type UserRepository interface {
    FindByID(id int) (*User, error)
}

type MySQLUserRepository struct {
    db *sql.DB
}

type UserService struct {
    repo UserRepository  // 接口值,内部包含指针
}

func NewUserService(repo UserRepository) *UserService {
    return &UserService{repo: repo}
}

值传递的适用场景

值传递仅适用于小型、不可变的数据结构:

type Config struct {
    Port    int
    Timeout int
}

// 对于小型配置,值传递也可以接受
type Server struct {
    config Config
}

总结

在Go语言的依赖注入中,对于服务、存储、数据库连接等有状态的依赖项,应该使用指针传递。这符合Go语言的惯用法,能够提供更好的性能和确保状态一致性。

回到顶部