我在操场上创建了这个:https://play.golang.org/p/Jj4UhA8Yn7
我也会粘贴下面的代码。
问题在于我的可组合性方法是否应该被认为是可行的,良好的Go代码,或者如果我错误地考虑它并且应该考虑更符合惯用Go的东西。
我的目标是使用此模式创建“逻辑”层,使用包装层不需要了解的其他逻辑来装饰底层。
作为一个粗略的例子,我可能会有这些“层”
我希望自己有点清楚,下面的示例代码比上面的话更能说明问题。
package main
import (
"errors"
"fmt"
)
//An interface
type Weird interface {
Name() string
SetName(name string) error
Age() int
SetAge(age int) error
}
//Simple struct to hold data
type SimpleWeird struct {
name string
age int
}
func (s *SimpleWeird) Name() string {
return s.name
}
func (s *SimpleWeird) SetName(name string) error {
s.name = name
return nil
}
func (s *SimpleWeird) Age() int {
return s.age
}
func (s *SimpleWeird) SetAge(age int) error {
s.age = age
return nil
}
//RegularWeird encapsulates some "business" logic within it's methods
//and would be considered normal logic flow
type RegularWeird struct {
Weird
}
func (r *RegularWeird) SetName(name string) error {
if len(name) > 5 {
return errors.New("Regulars can't set a name longer than 5 characters long")
}
return r.Weird.SetName(name)
}
func (r *RegularWeird) SetAge(age int) error {
if age > 80 {
return errors.New("Regulars can't set an age above 80")
}
return r.Weird.SetAge(age)
}
//AdminWeird encapsulates some admin "business" logic within it's methods
//It would be considered admin logic flow/rules
type AdminWeird struct {
Weird
}
//AdminWeirds don't have their own SetName. If they
//Wrap a SimpleWeird then any name size is allowed (desired behavior)
//If the wrap a regular weird then the regular weird's logic is enforced
func (a *AdminWeird) SetAge(age int) error {
if age > 100 {
return errors.New("Admins can't set an age above 100")
}
return nil
}
func NewAdminWeird() Weird {
return &AdminWeird{Weird: &SimpleWeird{}}
}
func NewRegularWeird() Weird {
return &RegularWeird{Weird: &SimpleWeird{}}
}
//This one doesn't make sense for this example but I wanted to show
//the composability aspect of this. I would be creating chainable
//interfaces that each handle different unrelated logic
func NewAdminRegularWeird() Weird {
return &AdminWeird{Weird: NewRegularWeird()}
}
func checkErr(err error) {
if err != nil {
fmt.Println(err)
}
}
func main() {
var err error
r := NewRegularWeird()
a := NewAdminWeird()
ar := NewAdminRegularWeird()
fmt.Println("Regular output:")
err = r.SetName("test")
checkErr(err)
err = r.SetAge(5)
checkErr(err)
err = r.SetName("something-longer")
checkErr(err)
err = r.SetAge(90)
checkErr(err)
fmt.Println("Admin output:")
err = a.SetName("test")
checkErr(err)
err = a.SetAge(5)
checkErr(err)
err = a.SetName("something-longer")
checkErr(err)
err = a.SetAge(101)
checkErr(err)
fmt.Println("AdminRegular output:")
err = ar.SetName("test")
checkErr(err)
err = ar.SetAge(5)
checkErr(err)
err = ar.SetName("something-longer")
checkErr(err)
err = ar.SetAge(90)
checkErr(err)
}
答案 0 :(得分:1)
我不确定惯用语,但我认为您已经有效地使用了接口,结构和构造方法来实现您的目标。如果适合你,可能会有一些改变。
1)让实现处理setter。
//An interface
type Weird interface {
Name() string
Age() int
}
实施者可以选择实现SetName或SetAge,但他们也可以使用值初始化或直接分配给结构。无论好坏,关于最小/最大年龄等的业务规则都不能通过接口强制执行,因此接口方法对我来说似乎是额外的代码。
2)创建一个管理界面,虽然您还没有真正定义示例中管理员的任何独特行为:
type Admin interface {
Weird
SomeAdminMethod() string
}
3)您使用接口作为所有New *函数的返回值对我来说有点混乱。这似乎更适合单个NewWeird函数,如:
func NewWeird(flavor string) (Weird, error) {
switch flavor {
case "regular":
return &RegularWeird{Weird: &SimpleWeird{}}, nil
case "admin":
return &AdminWeird{Weird: &SimpleWeird{}}, nil
case "regularadmin":
return &RegularWeird{Weird: &NewWeird(regular)}, nil
case default:
return nil, errors.Error("unknown weird type")
}
}
如果你想初始化一堆异构的怪异并将它们填充到[]Weird
或类似的东西中,那么使用接口作为返回是很方便的。
希望它有所帮助...