Basic API in golang antipattern?

孤者浪人 提交于 2021-01-29 22:34:13

问题


Correct me if I'm wrong, but for my understanding of an API is that it is something that allows me to modify and request data through an interface, which is what I want to do in Go. For example I have a user interface:

interface IUser {
  GetId() int
  GetName() string
  GetSignupDate() time
  GetPermissions() []IPermission
  Delete()
}

This already looks to me like active record and if I want to create a new user with a new id I would have to use new since Go doesn't support static functions as far as I know. This means I would also need a commit function in my interface, which makes it even worse for me. What am I doing wrong here?


回答1:


In Go, interfaces are behavioural. That is, they describe what a thing does more than what it is. Your example looks like you're trying to write C# in Go, with your heavy use of I in front of interface classes. However, an interface that is only implemented by one type is a bit of a waste of time.

Instead, consider:

interface Deleteable {  // You'd probably be tempted to call this IDeleteable
                        // Effective go suggests Deleter, but the grammar 
                        // sounds weird
    Delete() err
}

Now you can create a function to perform batch deletes:

func BatchDelete(victims []Deleteable) {
    // Do some cool things for batching, connect to db, start a transaction
    for _, victim := range(victims) {
        victim.Delete()  // Or arrange for this function to be called somehow.
    }
}

You'd probably get started faster by creating an interface for Update, Serialize and so on, and storing your actual users/permissions/etc in concrete structs that implement those methods. (Note in Go you don't have to say that a type implements an interface, it happens "automatically"). You also don't have to have a single interface for each method (Updater, Serializable), but you can bundle them all into one interface:

type DBObject interface {
    Update()
    Serialize() RowType
    Delete()
}

type User struct {
    Id int
    Name string
    // ... etc
}

Remember, your model can always "Fill in" a User object to return from your API, even if the actual representation of the User object is something much more diffuse, e.g. RDF triples.




回答2:


I agree with @ZanLynx comments. Go’s standard library seems to favour the interface way for APIs.

package main

import "fmt"

type S string
type I interface{ M() S }
func (s S) M() S { return s }
func API(i I)            I { return i.M() }

func main() {
  s := S("interface way")
  fmt.Println(API(s))
}

It may be worth noting that APIs that take in a one-method interface could be re-written as taking a function type.

package main

import "fmt"

func API(f func() string) string { return f() }

func main() {
  f := func() string { return "higher-order way" }
  fmt.Println(API(f))
}

As an API author, one could provide both mechanisms and let the API consumer decide the style of invocation. See http://aquaraga.github.io/functional-programming/golang/2016/11/19/golang-interfaces-vs-functions.html.



来源:https://stackoverflow.com/questions/31093981/basic-api-in-golang-antipattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!