定义一个具有接口返回类型的接口方法

huangapple go评论81阅读模式
英文:

Defining an interface method with interface return type

问题

TLDR 这是一个演示问题的游乐场:https://play.golang.org/p/myQtUVg1iq

我正在创建一个 REST API,并且有许多类型的资源可以通过 GET 请求获取:

GET http://localhost/api/users
GET http://localhost/api/groups

我有一个模型包,用于抽象不同资源的实现方式:

func(m *UserManager) Get() []Users {
    // 内部逻辑,假设返回正确的结果
}

func(m *GroupManager) Get() []Groups {
    // 内部逻辑,假设返回正确的结果
}

一个路由文件设置所有的路由和处理程序:

users := models.UserManager{}
groups := models.GroupManager{}

func GetUsersHandler (w http.ResponseWriter, r *http.Request) {
    users := users.Get()
    // 实现细节,将结果以 JSON 格式写入 w
}

func GetGroupsHandler (w http.ResponseWriter, r *http.Request) {
    groups := groups.Get()
    // 实现细节,将结果以 JSON 格式写入 w
}

func registerRoutes(r *mux.Router) {
    r.handleFunc("/api/users", GetUsersHandler).Method("GET")
    r.handleFunc("/api/groups", GetGroupsHandler).Method("GET")
}

我试图通过创建一个接口,然后只需要一个 GetHandler 来使其更通用。类似这样:

type Getter interface {
    Get() []interface{}
}

func GetHandler(g Getter) {
    return func(w http.ResponseWriter, r *http.Request) {
        results := g.Get()
        // 实现细节,将结果以 JSON 格式写入 w
    }
}

func registerRoutes(r *mux.Router) {
    r.handleFunc("/api/users", GetHandler(&users)).Method("GET")
    r.handleFunc("/api/groups", GetHandler(&groups)).Method("GET")
}

这非常接近工作,唯一的问题是模型的返回类型是特定的对象类型,但接口只使用了接口返回类型。有没有办法在不使模型返回 []interface{} 的情况下解决这个问题?

https://play.golang.org/p/myQtUVg1iq

英文:

TLDR Here is a playground that demonstrates the issue if you try to run it: https://play.golang.org/p/myQtUVg1iq

I am making a REST API and have many types of resources that can be retrieved via a GET request

GET http://localhost/api/users
GET http://localhost/api/groups

I have a models package which abstracts how the different resources are implemented:

func(m *UserManager) Get() []Users {
    // Internal logic, assume returns correct results
}

func(m *GroupManager) Get() []Groups {
    // Internal logic, assume returns correct results
}

A routes file setups all the routes and handlers:

users := models.UserManager{}
groups := models.GroupManager{}

func GetUsersHandler (w http.ResponseWriter, r *http.Request) {
    users := users.Get()
    // Implementation details, writing to w as JSON
}

func GetGroupsHandler (w http.ResponseWriter, r *http.Request) {
    groups := groups.Get()
    // Implementation details, writing to w as JSON
}

func registerRoutes(r *mux.Router) {
    r.handleFunc("/api/users", GetUsersHandler).Method("GET")
    r.handleFunc("/api/groups", GetGroupsHandler).Method("GET")
}

I am trying to make this more generic by creating an interface and then only needing a single GetHandler. Something like this:

type Getter interface {
    Get() []interface{}
}

func GetHandler(g Getter) {
    return func(w http.ResponseWriter, r *http.Request) {
        results := g.Get()
        // Implementation details, writing to w as JSON
    }
}

func registerRoutes(r *mux.Router) {
    r.handleFunc("/api/users", GetHandler(&users)).Method("GET")
    r.handleFunc("/api/groups", GetHandler(&groups)).Method("GET")
}

This is really close to working, the only problem is the return type from the models is a specific object type, but the interface just uses the interface return type. Is there any way to solve this without making the models return []interface{}?

https://play.golang.org/p/myQtUVg1iq

答案1

得分: 0

尝试不要像处理其他面向对象编程语言那样处理这个问题。在Go语言中,你不能拥有协变容器,所以你要么使用空的interface{},要么以不同的方式组织你的程序。

如果你的Get方法是不同的,并且你想在一个接口中对类型进行分组,可以使用另一种方法(有时我们甚至为接口添加空操作方法),或者将usersgroups作为interface{}传入。你仍然需要在调用链中的某个地方进行类型切换或断言,一旦你知道了类型,就可以相应地处理它。

没有更多的代码很难确定,但在这种情况下,最简单的方法可能就是让每种类型本身成为http.Handler,然后它可以相应地进行分发。

英文:

Try not to approach the problem like you would other OOP languages. You can't have covariant containers in Go, so you either have to use an empty interface{}, or you have to structure your program differently.

If your Get methods are different and you want to group types in an interface, use another method (sometimes we even have noop methods just for interfaces), or just pass in users or groups as an interface{}. You'll need to do a type switch or assertion at some point in the call chain anyway, and once you know what type it is you can handle it accordingly.

It's hard to tell without more code, but in this case, the easiest path may just be to have each type be an http.Handler itself, and it can dispatch accordingly.

答案2

得分: 0

我最终完全避免了这个问题,而是使用了Go 1.4中的新功能go generate来创建每个资源所需的代码,而不是试图减少我使用的代码量。

英文:

I ended up avoiding this problem entirely and instead of trying to reduce the amount of code I am using the new go generate feature in Go 1.4 to create the code that is necessary for each resource.

huangapple
  • 本文由 发表于 2014年12月25日 03:18:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/27641335.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定