Golang 工厂方法

问题描述 投票:0回答:3

我正在学习 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 constructor
3个回答
9
投票

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 语言中习惯的方式不完全相同。


4
投票

在您的情况下,您不能在“数据字段”类型上设置方法,而是这样做:

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

3
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.