在 Go 中包装多个实现
Wrapping multiple implementations in Go
我有一个应用程序有多个并发实现相同的 API(例如,一个由 SQL 数据库支持,另一个由存储在 XML 文件中的数据集支持)。我真正想做的是为 API that
中的每种事物定义一个父类型
保存所有实现通用的成员变量,
定义所有实现必须具备的方法。
所以,在(无效的)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
.
我有一个应用程序有多个并发实现相同的 API(例如,一个由 SQL 数据库支持,另一个由存储在 XML 文件中的数据集支持)。我真正想做的是为 API that
中的每种事物定义一个父类型保存所有实现通用的成员变量,
定义所有实现必须具备的方法。
所以,在(无效的)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
.