我的可组合性方法是 Go 惯用的吗?

Is my approach to composability idiomatic for Go?

我在操场上创建了这个:https://play.golang.org/p/Jj4UhA8Yn7

我也会把代码贴在下面。

问题围绕着我的可组合性方法是否是我应该考虑的可行的、好的 Go 代码,或者我是否错误地考虑它并且应该考虑更符合惯用的 Go 的东西。

我的目标是使用此模式创建 "logic" 层,用包装层不需要知道的额外逻辑来装饰底层。

作为一个粗略的例子,我可能有这些 "layers"

  1. 接口层 - 定义 "model"
  2. 的一组接口
  3. 简单的结构层 - 只保存来自数据库的数据满足上面的接口
  4. 验证层 - 包装来自接口层的接口并根据一些验证规则验证传入数据,然后将方法调用转发到下面的包装层。它还满足与它所包裹的层相同的界面。
  5. 翻译层 - 与第 3 层相同,通过首先调用它正在包装的层来翻译正在访问的任何文本,然后翻译文本,returns 翻译后的文本。它将遇到它包装的对象的相同接口。任何与获取信息无关的方法都会透明转发到底层

我希望我已经讲清楚了,下面的示例代码将比我上面的话更好地说明它。

来自 playgroud 的示例代码

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)

}

我不确定是否符合习惯,但我认为您已经有效地使用了接口、结构和构造函数方法来实现您的目标。如果适合您,您可以更改以下几项内容。

1) 让实现处理设置器。

//An interface
type Weird interface {
    Name() string

    Age() int
}

实施者可以选择实施 SetName 或 SetAge,但他们也可以使用值进行初始化或直接分配给结构。不管是好是坏,有关 min/max 年龄等的业务规则不能由接口强制执行,因此接口方法对我来说似乎是额外的代码。

2) 创建一个 Admin 界面,尽管您还没有在示例中真正定义 Admins 的任何独特行为:

type Admin interface {
    Weird
    SomeAdminMethod() string
}

3) 您将接口用作所有 New* 函数的 return 值让我有点困惑。这似乎更适合单个 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")
    }
}

如果你想初始化一堆异构的 Weirds 并将它们塞进 []Weird 或类似的东西,使用接口作为 return 是很方便的。

希望对您有所帮助...