多数据库驱动场景:将接口作为类型传递

场景:我有一个数据库类型实现了一些 CRUD 操作。

我想:

  • 抽象出数据库层以支持多个数据库

  • 将缓存层“放在它前面”作为通读缓存

所以我的想法是:

  • 为 postgres 特定代码和缓存创建数据库接口

  • 将 postgres 类型传递给缓存构造函数,以便它可以从数据库中使用它

请参阅下面的内容,了解我在分配时导致 cacheNew() 错误:

不能使用 dbdriver(接口 {} 类型的变量)作为结构文字中的数据库值:缺少方法 GetUser

解决这个问题的最佳方法是什么?

package main


import "fmt"


type database interface {

    GetUser(string)

}


type postgres struct {

    hostname string

}


func (p *postgres) GetUser(u string) {

    fmt.Printf("Postgres: GetUser %s\n", u)

}


type cache struct {

    db database

}


func cacheNew(dbdriver interface{}) cache {

    return cache{

        db: dbdriver,

    }

}


func (c *cache) GetUser(u string) {

    fmt.Printf("Cache: GetUser %s\n", u)


    c.db.GetUser(u)

}


func main() {

    // var db database


    db := postgres{

        hostname: "x",

    }

    db.GetUser("joe")


    dbViaCache := cacheNew(db)

    dbViaCache.GetUser("joe")

}


哆啦的时光机
浏览 105回答 3
3回答

繁华开满天机

在函数中使用database而不是interface{}cacheNewfunc cacheNew(dbdriver database) cache {postgres没有实现database,因为GetUser方法有指针接收器。因此,将postgres类型变量的地址发送到cacheNewdbViaCache := cacheNew(&db)

梦里花落0921

无论是基于意见的,我都不建议混合使用数据库和缓存功能。我可以推荐以下内容:首先创建包cache,然后从接口开始cache.go:// Cache is an interface which abstracts cache providers details for testability and usabilitytype Cache interface {    // Get retrieves the content associated with the given key. decoding it into the given    // pointer.    //    // Returns:    //   - nil if the value was successfully retrieved and ptrValue set    //   - ErrCacheMiss if the value was not found in the cache    //   - an implementation specific error otherwise    Get(key string, ptrValue interface{}) error    // set the given key/value in the cache, overwriting any existing value    // associated with that key    Set(key string, ptrValue interface{}, expires time.Duration) error}然后你可以在这个包中创建文件,比如inmem.go,,redis.go实现这个接口的所有其他东西。然后,您可以准备 YAML 配置,指示您要使用的缓存提供程序,并在服务器启动期间对其进行实例化。cache:  active: "redis"  redis:    address: "127.0.0.1:6379"    password: #     poolSize: #对于数据库,一切都比较棘手。根据我的经验,一次选择数据库提供程序并使用jmoiron/sqlx甚至本机驱动程序会更容易,例如用于高负载处理程序的纯pgx 。由于缺乏泛型和相对较慢的反射,除了基本的 CRUD 之外,大多数情况下对数据库提供者的抽象是不切实际的。对于非常简单的情况,您可以模仿存储库模式type UserRepository interface {    Get(ctx context.Context, id uint64) (*User, error)}type userRepository struct {    db *sqlx.DB}func (r *userRepository) Get(ctx context.Context, id uint64) (*User, error) {    const query = `SELECT id, email, login, language_id FROM users WHERE id = $1`    user := &User{}    if err := r.db.GetContext(ctx, user, query, id); err != nil {        return nil, err    }    return user, nil}然后在您的服务中,您将拥有类似的东西    key := "app:users#23"    user := &User{}    if err := svc.cache.Get(key, user); err != nil {        user, err = svc.userRepo.Get(userID)        if err != nil {            // handle         }        // otherwise, set cache        go svc.cache.Set(key, user, time.Minutes * 15)    }    // perform other actions with user 希望能帮助到你!

繁花如伊

为了解决您的问题,添加 newPostgres 构造函数。下面我对您的初始代码进行了更正。package mainimport "fmt"type database interface {    GetUser(string)}type postgres struct {    hostname string}func newPostgres(hostname string) *postgres {    return &postgres{        hostname: hostname,    }}func (p *postgres) GetUser(u string) {    fmt.Printf("Postgres: GetUser %s\n", u)}type cache struct {    db database}func cacheNew(db database) cache {    return cache{        db: db,    }}func (c *cache) GetUser(u string) {    fmt.Printf("Cache: GetUser %s\n", u)    c.db.GetUser(u)}func main() {    db := newPostgres("x")    db.GetUser("joe")    dbViaCache := cacheNew(db)    dbViaCache.GetUser("joe")}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go