在Go中包装多个实现

Sco*_*ter 2 struct interface dry embedding go

我有一个应用程序,该应用程序具有相同API的多个并发实现(例如,一个由SQL数据库支持,另一个由XML文件中存储的数据集支持)。我真正想做的是为API中的每种事物定义一个父类型

  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
}
Run Code Online (Sandbox Code Playgroud)

但是,这当然是不合法的,因为只有结构具有成员变量,只有接口具有成员函数。我可以只使用如下接口:

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
}
Run Code Online (Sandbox Code Playgroud)

和其他实现类似。有没有一种选择不强迫我将成员变量转换为成员函数?这种用例的最佳实践是什么?

icz*_*cza 5

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

type Person interface {
    PersonName() string
    PersonTitle() string
    PersonPosition() string
    Boss() (Person, error)
}
Run Code Online (Sandbox Code Playgroud)

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

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 }
Run Code Online (Sandbox Code Playgroud)

(注意:*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
}
Run Code Online (Sandbox Code Playgroud)

*PersonXML确实实现了Person

使用它的例子:

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

输出(在Go Playground上尝试):

Bob
Run Code Online (Sandbox Code Playgroud)

要创建PersonSQL类型,只需再次Boss()嵌入即可*BasePerson

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
}
Run Code Online (Sandbox Code Playgroud)

*PersonSQL再次执行Person