我有一个应用程序,它具有同一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
}
但是,当然,这不合法,因为只有结构有成员变量,只有接口有成员函数。我可以用这样的接口来做到这一点:
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
}
和其他实现类似。有没有一种替代方法不会迫使我将成员变量转换为成员函数?这种用例的最佳实践是什么?
答案 0 :(得分:4)
最佳做法是提供界面:
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
类型,只需在嵌入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
}
*PersonSQL
再次实施Person
。
答案 1 :(得分:0)
我们可以应用第一种方法,因为struct可以将接口与字段一起嵌入。如果你看一下包Kill
它使用相同的方法在界面中嵌入一个接口。
// 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)
}
所以你的方法绝对有效。您可以使用struct receiver轻松实现接口。
{{1}}