在 Go 中包装多个实现 [英] Wrapping multiple implementations in Go
问题描述
我有一个应用程序,它具有相同 API 的多个并发实现(例如,一个由 SQL 数据库支持,另一个由存储在 XML 文件中的数据集支持).我真正想做的是为 API 中的每种类型的事物定义一个父类型
I have an application that has multiple concurrent implementations of the same API (e.g. one backed by a SQL database and another by a dataset stored in an XML file). What I'd really like to do is to define a parent type for each type of thing in the API that
保存所有实现通用的成员变量
holds the member variables that are common to all of the implementations and
定义所有实现必须具有的方法.
defines the methods that all implementations must have.
所以,在(无效的)Go 中,我想做一些类似的事情:
So, in (invalid) Go, I want to do something like:
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
}
但是,当然,这是不合法的,因为只有结构才有成员变量,只有接口有成员函数.我可以只用这样的接口来做到这一点:
But, of course, that's not legal since only structs have member variables and only interfaces have member functions. I could do this with just interfaces like this:
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
}
其他实现也类似.是否有替代方法不会强迫我将成员变量转换为成员函数?这种用例的最佳做法是什么?
and similarly for other implementations. Is there an alternative that doesn't force me to turn member variables into member functions? What's best practice for such a use case?
推荐答案
最佳实践是提供一个接口:
Best practice would be to provide an interface:
type Person interface {
PersonName() string
PersonTitle() string
PersonPosition() string
Boss() (Person, error)
}
并且还提供一个结构体,其中包含公共字段和获取它们的方法:
And also provide a struct which contains the common fields and the methods to get them:
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()
方法.)
(Note: *BasePerson
itself does not implement Person
as it doesn't have a Boss()
method.)
任何嵌入*BasePerson
的类型都会自动提升的方法,因此要实现Person
,只有Boss()
方法需要添加.
Any type that embeds *BasePerson
will automatically have its methods promoted, and so to implement Person
, only the Boss()
method will need to be added.
例如:
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 上试试):
Output (try it on the Go Playground):
Bob
要创建 PersonSQL
类型,如果您嵌入 *BasePerson
,则只需再次添加 Boss()
方法:
To create the PersonSQL
type, you again only have to add the Boss()
method if you embed *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
.
这篇关于在 Go 中包装多个实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!