我想创建一个接口,使添加新的存储后端变得容易。
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也应该满足存储接口,但我得到的是:
./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我做错了什么?我希望我在这里遗漏了一些基本的东西。
发布于 2017-08-07 07:31:56
这段代码一点意义也没有。您要么实现了一个绑定到工厂将要生成的结构的工厂模式,要么以错误的方式重新发明了轮子,重新实现了已经存在的关键字,并将其绑定到一个结构,该结构在您使用它的时候是nil的。
您可以去掉helper函数并简单地使用
s := new(S3)
f := new (File)也可以使用静态工厂函数,如下所示:
// Do NOT tie your Factory to your type
function New() S3 {
return S3{}
}或者,似乎更适合您的用例,创建一个工厂接口,实现它,并让它的New()函数返回一个Storage实例:
type StorageFactory interface {
New() Storage
}
type S3Factory struct {}
function (f *S3Factory) New() Storage {
return S3{}
}有多种方法可以注册您的工厂。可以使用全局var和init
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包中的某个位置
func init() {
Registry["s3"] = function(){ return S3Factory{}}
}你甚至可以考虑让工厂接受参数。
发布于 2017-08-07 05:11:56
我喜欢你在这里所做的事情,而且我实际上参与过一些涉及非常相似的设计挑战的项目,所以我希望我的建议能对你有所帮助。
为了满足接口要求,您需要从...更新代码。
// New returns a new S3
func (S3) New() (newS3 S3) {
newS3 = S3{}
return newS3
}到这个
// New returns a new S3
func (S3) New() (newS3 Storage) {
newS3 = S3{}
return newS3
}可以这么说,这意味着您将收到Storage的一个实例。如果您希望在不使用type assertion情况下访问S3中的任何内容,最好在接口中公开S3函数/方法。
因此,假设您想要一种在S3客户端中列出对象的方法。支持这一点的一个好方法是更新存储接口以包含List,并更新S3以使其具有自己的List实现:
// 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) -这很好!一旦您以所需的方式使用了此列表函数,添加更多的客户端/提供者将更像是开发插件,而不是实际编写/架构代码(因为此时您的设计已经完成)。
令人满意的接口的真正关键是让签名精确匹配,并将接口视为一系列常见的函数/方法,您希望每种存储提供程序类型都能处理这些函数/方法,以满足您的目标。
如果你有任何问题,或者我写的任何东西不清楚,请发表意见,我很乐意澄清或调整我的帖子:)
https://stackoverflow.com/questions/45534750
复制相似问题