从接口创建处理程序

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

Creating handlers from an interface

问题

假设我有这个接口:

type Selecter interface {
    Select(vars ...string) error
}

我想创建处理程序,它们在调用Select函数后,基本上只返回该接口的JSON形式。像这样:

func MakeHandler(s Selecter) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        //... 做一些获取变量的操作 ...
        if err := s.Select(v1, v2); err != nil {
            //... 处理错误 ...
        }
        b, err := json.Marshal(s)
        if err != nil {
            //... 处理错误 ...
        }
        w.Write(b)
    }
}

所以如果CoolType是一个Selecter,我可以这样做:

type CoolType struct {
    CoolString string `json:"cool_string"`
    CoolInt    int    `json:"cool_int"`
    CoolBool   bool   `json:"cool_bool"`
}

func (c *CoolType) Select(vars ...string) error {
    // 使用变量填充c
    return nil
}

fn := MakeHandler(CoolType{})

我对此的基本问题是s是一个接口并且使用了指针。这会导致在调用Select和调用Marshal之间,s可能会在goroutine中被修改,从而使其不安全。

我真的认为这是我想要实现的方式,因为它相当简洁且易于更改,但我觉得我可能漏掉了一些东西。我可以使用reflect或更改Selecter接口,使其Select方法返回一个interface{},因为我不特别关心类型是什么。然后,我只需在每个Select实现中创建该类型的新副本。或者可以使用互斥锁。或者也许更好的方法是让我所有的Selecter类型都实现ServeHTTP并成为http.Handler

无论如何,我认为人们可能已经尝试过类似的事情,并提出了可能更优雅的解决方案,所以我想听听一些想法。

英文:

Let's say I have this interface

type Selecter interface {
	Select(vars ...string) error
}

and I want to make handlers that pretty much just return JSON forms of that interface after it has called the Select function. Like this:

func MakeHandler(s Selecter) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//... do some stuff to get vars ...
		if err := s.Select(v1, v2); err != nil {
			//... blah blah errors ...
		}
		b, err := json.Marshal(s)
		if err != nil {
			//... blah blah errors ...
		}
		w.Write(b)
	}
}

So if CoolType is a Selecter I can do something like this

type CoolType struct {
	CoolString string `json:"cool_string"`
	CoolInt int 	  `json:"cool_int"`
	CoolBool bool 	  `json:"cool_bool"`
}

func (c *CoolType) Select(vars ...string) error {
	// fill up c using vars
	return nil
}

// this looks kinda ugly to me too
fn := MakeHandler(CoolType{})

The underlying problem I have with this is that s is an interface and uses a pointer. This would make this not safe in goroutines since s could be modified between the calls to Select and the call to Marshal.

I really think this is the way I'd like to go about implementing this since it is fairly concise and easy for me to change, but I think I'm missing something. I could use reflect or change the Selecter interface to have Select return an interface{} instead since I don't particularly care what the type is. Then I'd just make a new copy of the type in every implementation of Select I guess. Or a mutex would work. Or perhaps the better way to do this would be to have all of my Selecter types just implement ServeHTTP and be an http.Handler.

Anyway I assume people have tried something like this and have come up with possibly more elegant solutions so I'd like to hear some ideas.

答案1

得分: 1

如果你担心变异,可以传递值的副本。为此,你可能需要更改接口,类似于以下方式:

type Selecter interface {
    Select(vars ...string) (Selecter, error)
}

并将你的Select方法更改为使用值接收器来满足接口。它应该是 func (c CoolType) Select(vars ...string) (CoolType, error)

然后,将你的s作为值而不是指针传递,并像这样调用Select:

if s, err := s.Select(v1, v2); err != nil {
     //... 处理错误 ...
}

你可以争论说你会丢失有关类型的信息,但是在将值作为Selecter接口传递时,你已经丢失了它。

总的来说,你的实现很不错。我觉得在Go语言中,优雅与其他语言中的优雅有所不同。我会说,在Go语言中,优雅意味着可读性、可维护性和准确性,而不是官僚主义和试图保护你免受自己的影响。

英文:

If you are afraid of mutations pass a copy of the value. To do it you probably have to change your interface to something like:

type Selecter interface {
    Select(vars ...string) (Selecter, error)
}

And change your Select method to take a value receiver to fulfill the interface. It would be func (c CoolType) Select(vars ...string) (CoolType, error)

Then pass your s as a value instead of pointer and call Select like:

if s, err := s.Select(v1, v2); err != nil {
     //... blah blah errors ...
}

You can argue that you lose information about the type, but you already lost it while passing the value as a Selecter interface.

But in general your implementation is good. My feeling is that in Go elegant is something different than in other languages. I'd say that in Go elegant is readable, maintainable and accurate - not bureaucracy and trying to protect you from yourself.

huangapple
  • 本文由 发表于 2016年1月12日 14:42:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/34737124.html
匿名

发表评论

匿名网友

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

确定