Sco*_*ter 2 struct interface dry embedding go
我有一个应用程序,该应用程序具有相同API的多个并发实现(例如,一个由SQL数据库支持,另一个由XML文件中存储的数据集支持)。我真正想做的是为API中的每种事物定义一个父类型
持有所有实现共有的成员变量,并且
定义所有实现必须具有的方法。
因此,在(无效)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)
和其他实现类似。有没有一种选择不强迫我将成员变量转换为成员函数?这种用例的最佳实践是什么?
最佳做法是提供一个接口:
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。