英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论