Golang工厂方法

时间:2018-04-07 21:52:57

标签: go constructor

我正在学习golang,并且陷入了一个非常简单的概念。也许我的OO习惯使我的理解蒙上阴影,但我似乎无法让这个简单的例子起作用:

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)创建数据字段的正确方法是什么?

3 个答案:

答案 0 :(得分:3)

通过在函数签名上添加(d datafield),您将使数据字段成为接收器参数(https://tour.golang.org/methods/1),因此NewDatafield现在是数据字段的方法。这意味着要调用它你需要已经有一个没有多大意义的数据字段,所以你可以删除(d数据字段),它可以工作,甚至更好直接构建stuct(https://gobyexample.com/structs) :

field := datafield{"name", "value"}

当需要完成额外的工作时,您才真正需要构造函数,而不仅仅是设置字段。

答案 1 :(得分:2)

您不能在类型"数据字段"上设置方法。在你的情况下,但改为:

func NewField(name, value string) *datafield {
    retval := new(datafield)
    retval.name = name
    retval.value = value
    return retval
}

答案 2 :(得分:2)

Go并不是完全面向对象的语言并且提升了简洁性,这就是为什么之前的答案都集中在修复代码上。不过,如果你真的需要在Go中实现这个设计模式,请进一步阅读。

  

工厂方法模式是使用工厂的创建模式   处理无需创建对象的问题的方法   指定将要创建的对象的确切类。这是   通过调用工厂方法创建对象来完成。 (wikipedia

这是Svetlin在Go中的另一个定义和一个非常好的例子 拉尔切夫:

  

Factory Method模式用于定义运行时接口   创建一个对象。它被称为工厂,因为它创造了各种各样的   对象的类型不一定知道它是什么样的对象   创建或如何创建它。 (Desing Patterns in Golang: Factory Method

我已经扩展了您的示例以演示使用工厂方法的好处,因为如果您正在处理一个结构(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

但是,如果查看主要功能,则找不到具体类型dataField1dataField2的任何提及或初始化。 所有复杂性都隐藏在dataFieldFactories之后。 dataField1FactorydataField2Factory都实现Create接口并返回dataField接口,这两种具体类型 也实施。因此,您可以为每种具体类型调用Print()

Document.Print()使用“创建”和“打印”界面进行打印 在没有任何关于如何实际创建或打印字段的任何知识的情况下输出所有字段。我们通过向Document结构(对象)提供工厂方法(dataField1Factory{}dataField2Factory{})列表以及相应的字符串值来实现这一目标。

请原谅我有点人为的例子,但我希望,你会得到基本的想法。 正如您所看到的,Go允许您实现您熟悉的设计模式,可能与您在纯OO语言中习惯的方式不同。