首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >golang API接口,我错过了什么?

golang API接口,我错过了什么?
EN

Stack Overflow用户
提问于 2017-08-07 01:33:53
回答 2查看 855关注 0票数 0

我想创建一个接口,使添加新的存储后端变得容易。

代码语言:javascript
复制
package main

// Storage is an interface to describe storage backends
type Storage interface {
    New() (newStorage Storage)
}

// File is a type of storage that satisfies the interface Storage
type File struct {
}

// New returns a new File
func (File) New() (newFile Storage) {
    newFile = File{}
    return newFile
}

// S3 is a type of storage that satisfies the interface Storage
type S3 struct {
}

// New returns a new S3
func (S3) New() (newS3 S3) {
    newS3 = S3{}
    return newS3
}

func main() {
    // List of backends to choose from
    var myStorage map[string]Storage
    myStorage["file"] = File{}
    myStorage["s3"] = S3{}

    // Using one of the backends on demand
    myStorage["file"].New()
    myStorage["s3"].New()
}

但是,似乎不可能定义和满足一个函数,该函数应该返回一个同时满足接口本身的对象。

File.New()返回一个满足存储类型的对象。

S3.New()返回一个S3类型的对象。S3也应该满足存储接口,但我得到的是:

代码语言:javascript
复制
./main.go:32: cannot use S3 literal (type S3) as type Storage in assignment:
    S3 does not implement Storage (wrong type for New method)
        have New() S3
        want New() Storage

我做错了什么?我希望我在这里遗漏了一些基本的东西。

EN

回答 2

Stack Overflow用户

发布于 2017-08-07 07:31:56

这段代码一点意义也没有。您要么实现了一个绑定到工厂将要生成的结构的工厂模式,要么以错误的方式重新发明了轮子,重新实现了已经存在的关键字,并将其绑定到一个结构,该结构在您使用它的时候是nil的。

您可以去掉helper函数并简单地使用

代码语言:javascript
复制
s := new(S3)
f := new (File)

也可以使用静态工厂函数,如下所示:

代码语言:javascript
复制
// Do NOT tie your Factory to your type
function New() S3 {
  return S3{}
}

或者,似乎更适合您的用例,创建一个工厂接口,实现它,并让它的New()函数返回一个Storage实例:

代码语言:javascript
复制
type StorageFactory interface {
  New() Storage
}

type S3Factory struct {}

function (f *S3Factory) New() Storage {
  return S3{}
}

有多种方法可以注册您的工厂。可以使用全局var和init

代码语言:javascript
复制
import "example.com/foo/storage/s3"

type FactoryGetter func() StorageFactory
type FactoryRegistry map[string] FactoryGetter

// Registry will be updated by an init function in the storage provider packages
var Registry FactoryRegistry

func init(){
  Registry = make(map[string] FactoryGetter)
}

// For the sake of shortness, a const. Make it abflag, for example
const storageProvider = "s3"

func main(){
  f := Registry[storageProvider]()
  s := f.New()
  s.List()
}

在S3包中的某个位置

代码语言:javascript
复制
func init() {
  Registry["s3"] = function(){ return S3Factory{}}
}

你甚至可以考虑让工厂接受参数。

票数 3
EN

Stack Overflow用户

发布于 2017-08-07 05:11:56

我喜欢你在这里所做的事情,而且我实际上参与过一些涉及非常相似的设计挑战的项目,所以我希望我的建议能对你有所帮助。

为了满足接口要求,您需要从...更新代码。

代码语言:javascript
复制
// New returns a new S3
func (S3) New() (newS3 S3) {
    newS3 = S3{}
    return newS3
}

到这个

代码语言:javascript
复制
// New returns a new S3
func (S3) New() (newS3 Storage) {
    newS3 = S3{}
    return newS3
}

可以这么说,这意味着您将收到Storage的一个实例。如果您希望在不使用type assertion情况下访问S3中的任何内容,最好在接口中公开S3函数/方法。

因此,假设您想要一种在S3客户端中列出对象的方法。支持这一点的一个好方法是更新存储接口以包含List,并更新S3以使其具有自己的List实现:

代码语言:javascript
复制
// Storage is an interface to describe storage backends
type Storage interface {
    New() (newStorage Storage)
    List() ([]entry, error) // or however you would prefer to trigger List
}

...

// New returns a new S3
func (S3) List() ([] entry, error) {
    // initialize "entry" slice
    // do work, looping through pages or something
    // return entry slice and error if one exists
}

当需要添加对Google Cloud Storage、Rackspace Cloud Files、Backblaze B2或任何其他对象存储提供商的支持时,它们中的每一个都需要实现List() ([] entry,error) -这很好!一旦您以所需的方式使用了此列表函数,添加更多的客户端/提供者将更像是开发插件,而不是实际编写/架构代码(因为此时您的设计已经完成)。

令人满意的接口的真正关键是让签名精确匹配,并将接口视为一系列常见的函数/方法,您希望每种存储提供程序类型都能处理这些函数/方法,以满足您的目标。

如果你有任何问题,或者我写的任何东西不清楚,请发表意见,我很乐意澄清或调整我的帖子:)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45534750

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档