Go的可解决方法是否适用于Go?

时间:2016-08-03 04:34:25

标签: go composition

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

我也会粘贴下面的代码。

问题在于我的可组合性方法是否应该被认为是可行的,良好的Go代码,或者如果我错误地考虑它并且应该考虑更符合惯用Go的东西。

我的目标是使用此模式创建“逻辑”层,使用包装层不需要了解的其他逻辑来装饰底层。

作为一个粗略的例子,我可能会有这些“层”

  1. 接口层 - 定义“模型”的一组接口
  2. 简单的结构层 - 只保存数据库中的数据符合上面的接口
  3. 验证层 - 从接口层包装接口并根据某些验证规则验证传入数据,然后将方法调用转发到下面的包装层。它还符合与包装层相同的界面。
  4. 翻译层 - 与数字3相同,通过首先调用它正在包装的图层来翻译正在访问的任何文本,然后翻译文本,并返回翻译后的文本。它将满足它包装的对象的相同接口。任何与获取信息无关的方法都将透明地转发到底层。
  5. 我希望自己有点清楚,下面的示例代码比上面的话更能说明问题。

    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 个答案:

答案 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或类似的东西中,那么使用接口作为返回是很方便的。

希望它有所帮助...