在 Go 中包装多个实现 [英] Wrapping multiple implementations in Go

查看:33
本文介绍了在 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

  1. 保存所有实现通用的成员变量

  1. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆