猿问

Golang 仓库模式

我尝试在 Go 应用程序(简单的 Web 服务)中实现存储库模式,并尝试找到更好的方法来避免代码重复。


这是一个代码


接口是:

type IRoleRepository interface {

    GetAll() ([]Role, error)

}


type ISaleChannelRepository interface {

    GetAll() ([]SaleChannel, error)

}

和实施:

func (r *RoleRepository) GetAll() ([]Role, error) {

        var result []Role

        var err error

        var rows *sql.Rows


        if err != nil {

            return result, err

        }


        connection := r.provider.GetConnection()

        defer connection.Close()


        rows, err = connection.Query("SELECT Id,Name FROM Position")

        defer rows.Close()


        if err != nil {

            return result, err

        }


        for rows.Next() {

            entity := new(Role)

            err = sqlstruct.Scan(entity, rows)


            if err != nil {

                return result, err

            }


            result = append(result, *entity)

        }

        err = rows.Err()

        if err != nil {

            return result, err

        }


        return result, err

    }


    func (r *SaleChannelRepository) GetAll() ([]SaleChannel, error) {

        var result []SaleChannel

        var err error

        var rows *sql.Rows


        if err != nil {

            return result, err

        }


        connection := r.provider.GetConnection()

        defer connection.Close()


        rows, err = connection.Query("SELECT DISTINCT SaleChannel 'Name' FROM Employee")

        defer rows.Close()


        if err != nil {

            return result, err

        }


        for rows.Next() {

            entity := new(SaleChannel)

            err = sqlstruct.Scan(entity, rows)


            if err != nil {

                return result, err

            }


            result = append(result, *entity)

        }

        err = rows.Err()

        if err != nil {

            return result, err

        }


        return result, err

    }

如您所见,差异仅在于几句话。我试图从 C# 中找到类似泛型的东西,但没有找到。


谁能帮我?


阿波罗的战车
浏览 294回答 3
3回答

墨色风雨

不,Go 没有泛型并且在可预见的未来不会有它们¹。您有三个选择:重构您的代码,以便您拥有一个接受 SQL 语句和另一个函数的函数,并且:在这种情况下,您将拥有一个通用的“查询”功能,区别仅在于“扫描”功能。这方面的几种变化是可能的,但我怀疑你有这个想法。使用提供的语句查询数据库。迭代结果的行。对于每一行,调用提供的函数,其任务是扫描该行。使用sqlx基本上是 SQL 驱动的数据库的包,什么encoding/json是 JSON 数据流:它使用对您的类型的反射来创建和执行 SQL 来填充它们。通过这种方式,您将在另一个层面上获得可重用性:您根本不会编写样板代码。使用代码生成,这是拥有“代码模板”的Go-native 方式(这就是泛型的意义所在)。通过这种方式,您(通常)编写一个 Go 程序,它接受一些输入(以您希望的任何格式),读取它并写出一个或多个包含 Go 代码的文件,然后编译这些文件。在您的非常简单的情况下,您可以从 Go 函数的模板和某种表开始,该表将 SQL 语句映射到要从所选数据创建的类型。我会注意到您的代码确实看起来非常单调。没有头脑正常的人会在 Go 中实现“存储库模式”,但是只要它能让你开心就好了——我们都在一定程度上被灌输了我们习惯的语言/环境——但是你的connection := r.provider.GetConnection()样子令人担忧:Godatabase/sql与“流行”的环境和框架截然不同,所以我强烈建议从this和this开始。¹(截至 2021 年 5 月 31 日更新)Go 将具有泛型,因为实现它们的提案已被接受,并且实现它们的工作正在进行中。

守候你守候我

如果我误解了,请原谅我,但更好的模式可能如下所示:type RepositoryItem interface {    Name() string // for example}type Repository interface {    GetAll() ([]RepositoryItem, error)}目前,对于每种类型的存储库,您本质上都有多个接口,因此除非您要实现多种类型的RoleRepository,否则您可能没有该接口。从长远来看,拥有泛型Repository和RepositoryItem接口可能会使您的代码更具可扩展性(更不用说更容易测试了)。一个人为的例子可能是(如果我们假设 aRepository与后端模糊地相关)诸如MySQLRepository和 之类的实现MongoDBRepository。通过抽象存储库的功能,您可以防止将来发生突变。

慕桂英546537

interface{}是 Go 中的“通用类型”。我可以想象做这样的事情:package mainimport "fmt"type IRole struct {    RoleId uint}type ISaleChannel struct {    Profitable bool}type GenericRepo interface{    GetAll([]interface{})}// conceptual repo to store all Roles and SaleChannelstype Repo struct {    IRoles        []IRole    ISaleChannels []ISaleChannel}func (r *Repo) GetAll(ifs []interface{}) {    // database implementation here before type switch     for _, v := range ifs {        switch v := v.(type) {        default:                fmt.Printf("unexpected type %T\n", v)        case IRole:                fmt.Printf("Role %t\n", v)                            r.IRoles = append(r.IRoles, v)        case ISaleChannel:                fmt.Printf("SaleChannel %d\n", v)                r.ISaleChannels = append(r.ISaleChannels, v)        }    }}func main() {    getter := new(Repo)    // mock slice    data := []interface{}{        IRole{1},        IRole{2},        IRole{3},        ISaleChannel{true},        ISaleChannel{false},        IRole{4},    }    getter.GetAll(data)    fmt.Println("IRoles: ", getter.IRoles)    fmt.Println("ISaleChannels: ", getter.ISales)}通过这种方式,您不必以两个结构和/或接口结束IRole和ISale
随时随地看视频慕课网APP

相关分类

Go
我要回答