在 Go 中包装多个实现

Wrapping multiple implementations in Go

我有一个应用程序有多个并发实现相同的 API(例如,一个由 SQL 数据库支持,另一个由存储在 XML 文件中的数据集支持)。我真正想做的是为 API that

中的每种事物定义一个父类型
  1. 保存所有实现通用的成员变量,

  2. 定义所有实现必须具备的方法。

所以,在(无效的)Go 中,我想做类似的事情:

type Person interface {
    Name string
    Title string
    Position string
    Boss() *Person
}

type Person_XML struct {
    Person
}

func (p *Person_XML) Boss() (*Person, error) {
    // Poke around an XML document and answer the question
    return boss, nil
}

type Person_SQL {
    Person
}

func (p *Person_SQL) Boss() (*Person, error) {
    // Do a DB query and construct the record for the boss
    return boss, nil
}

但是,当然,这是不合法的,因为只有结构有成员变量,只有接口有成员函数。我可以只用这样的界面来做到这一点:

type Person interface {
    Name() string
    Title() string
    Position() string
    Boss() Person
}

type Person_XML struct {
    NameValue string
    TitleValue string
    PositionValue string
    Person
}

func (p *Person_XML) Name() string {
    return p.NameValue
}

func (p *Person_XML) Title() string {
    return p.TitleValue
}

func (p *Person_XML) Position() string {
    return p.PositionValue
}

func (p *Person_XML) Boss() (Person, error) {
    // Poke around an XML document and answer the question
    return boss, nil
}

其他实现也类似。是否有替代方案不强制我将成员变量转换为成员函数?这种用例的最佳做法是什么?

我们可以应用第一种方法,因为结构可以将接口与字段一起嵌入。如果您查看包 sort,它使用相同的方法,将接口嵌入到结构中。

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}

type reverse struct {
    // This embedded Interface permits Reverse to use the methods of
    // another Interface implementation.
    Interface
}

所以你的做法是绝对有效的。您可以使用 struct receiver 轻松实现接口。

// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
        return r.Interface.Less(j, i)
}

最佳做法是提供一个接口:

type Person interface {
    PersonName() string
    PersonTitle() string
    PersonPosition() string
    Boss() (Person, error)
}

并且还提供了一个结构,其中包含公共字段 获取它们的方法:

type BasePerson struct {
    Name     string
    Title    string
    Position string
}

func (p *BasePerson) PersonName() string     { return p.Name }
func (p *BasePerson) PersonTitle() string    { return p.Title }
func (p *BasePerson) PersonPosition() string { return p.Position }

(注意:*BasePerson 本身没有实现 Person,因为它没有 Boss() 方法。)

任何嵌入 *BasePerson 的类型都会自动将其方法 提升 ,因此要实现 Person,只有 Boss() 方法会需要补充。

例如:

type PersonXML struct {
    *BasePerson
}

func (p *PersonXML) Boss() (Person, error) {
    // Poke around an XML document and answer the question
    var boss *PersonXML
    return boss, nil
}

*PersonXML 确实实现了 Person.

使用示例:

var p Person
p = &PersonXML{
    BasePerson: &BasePerson{
        Name:     "Bob",
        Title:    "sysadmin",
        Position: "leader",
    },
}
fmt.Println(p.PersonName())

输出(在 Go Playground 上尝试):

Bob

要创建 PersonSQL 类型,如果嵌入 *BasePerson:

,您只需再次添加 Boss() 方法
type PersonSQL struct {
    *BasePerson
}

func (p *PersonSQL) Boss() (Person, error) {
    // Do a DB query and construct the record for the boss
    var boss *PersonSQL
    return boss, nil
}

*PersonSQL 再次执行 Person.