我正在学习 golang,并且陷入了一个令人尴尬的简单概念。也许是我的面向对象习惯影响了我的理解,但我似乎无法让这个简单的例子发挥作用:
package main
import (
"fmt"
)
type datafield struct {
name string
value string
}
func (d datafield) NewField(name, value string) *datafield {
retval := new(datafield)
retval.name = name
retval.value = value
return retval
}
func main() {
field := datafield.NewField("name", "value")
if field == nil {
fmt.Println("Error: Did not create a datafield")
} else {
fmt.Println("Success!")
}
}
错误是:
prog.go:20:29: not enough arguments in call to method expression datafield.NewField
have (string, string)
want (datafield, string, string)
让
NewField(string,string)
创建数据字段的正确方法是什么?
Go 并不完全是面向对象的语言,它提倡简单性,这就是为什么前面的答案集中在修复代码上。不过,如果您确实需要在 Go 中实现此设计模式,请进一步阅读。
工厂方法模式是一种使用工厂的创建模式 方法来处理创建对象的问题,而不必 指定将创建的对象的确切类。这是 通过调用工厂方法创建对象来完成。 (维基百科)
这是 Svetlin 在 Go 中的另一个定义和一个非常好的例子 拉尔切夫:
工厂方法模式用于定义运行时接口 创建一个对象。它被称为工厂是因为它创造了各种 对象的类型,而不必知道它是什么类型的对象 创建或如何创建它。 (Golang 中的设计模式:工厂 方法)
我对您的示例进行了一些扩展,以演示使用工厂方法的好处,因为如果您正在处理一个结构(OO 世界中的一个对象),则根本不需要使用工厂。 https://goplay.space/#SOXPmM86GgF
package main
import (
"fmt"
)
type dataField interface {
Print()
}
type dataField1 struct {
name string
value string
}
func (df *dataField1) Print() {
fmt.Println("dataField1 ->", df.name, ":", df.value)
}
type dataField2 struct {
name string
value string
}
func (df *dataField2) Print() {
fmt.Println("dataField2 ->", df.name, ":", df.value)
}
type dataFieldFactory interface {
Create(name, value string) dataField
}
type dataField1Factory struct{}
func (factory *dataField1Factory) Create(name, value string) dataField {
return &dataField1{
name: name,
value: value,
}
}
type dataField2Factory struct{}
func (factory *dataField2Factory) Create(name, value string) dataField {
return &dataField2{
name: name,
value: value,
}
}
type Document struct {
dataFieldFactories []dataFieldFactory
allValues [][]string
}
func (doc *Document) Print() {
for i, factory := range doc.dataFieldFactories {
field := factory.Create(doc.allValues[i][0], doc.allValues[i][1])
field.Print()
}
}
func main() {
doc := &Document{
dataFieldFactories: []dataFieldFactory{
&dataField1Factory{},
&dataField2Factory{},
},
allValues: [][]string{{"name1", "value1"}, {"name2", "value2"}},
}
doc.Print()
}
程序只是打印这个
dataField1 -> name1 : value1
dataField2 -> name2 : value2
但是,如果您查看 main 函数,您不会发现任何具体类型的提及或初始化
dataField1
和 dataField2
。
所有的复杂性都隐藏在dataFieldFactories
的背后。
dataField1Factory
和dataField2Factory
都实现了Create
接口并返回dataField接口,这两种具体类型
也落实。因此,您可以为每种具体类型调用 Print()
。
Document.Print()
同时使用Create和Print接口进行打印
输出所有字段,而无需了解这些字段的实际创建或打印方式。我们通过提供工厂方法列表(dataField1Factory{}
和dataField2Factory{}
)和文档结构(对象)相应的字符串值来实现这一点。
请原谅我举了一个有点人为的例子,但我希望你能明白基本的想法。 正如您所看到的,Go 允许您实现您熟悉的设计模式,但可能与您在纯 OO 语言中习惯的方式不完全相同。
在您的情况下,您不能在“数据字段”类型上设置方法,而是这样做:
func NewField(name, value string) *datafield {
retval := new(datafield)
retval.name = name
retval.value = value
return retval
}
通过将 (d datafield) 添加到函数签名中,您可以使数据字段成为接收器参数 (https://tour.golang.org/methods/1),因此 NewDatafield 现在是数据字段上的方法。这意味着要调用它,您需要已经有一个没有多大意义的数据字段,因此您可以删除(d数据字段),它将起作用,甚至更好的是直接构建结构(https://gobyexample.com /结构):
field := datafield{"name", "value"}
只有在需要完成额外工作时才真正需要构造函数,而不仅仅是设置字段。