douyanpeng0748
2018-04-07 21:52 阅读 46
已采纳

高朗工厂法

I am learning golang, and am getting stuck on an embarassingly simple concept. Maybe it's my OO habits clouding my comprehension, but I can't seem to get this simple example to work:

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!")
    }
}

The error is:

prog.go:20:29: not enough arguments in call to method expression datafield.NewField
    have (string, string)
    want (datafield, string, string)

What is the proper way to get NewField(string,string) to create a datafield?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

3条回答 默认 最新

  • 已采纳
    dongleiwei2182 dongleiwei2182 2018-04-07 22:00

    You must not set a method on type "datafield" in your case, but do this instead :

    func NewField(name, value string) *datafield {
        retval := new(datafield)
        retval.name = name
        retval.value = value
        return retval
    }
    
    点赞 评论 复制链接分享
  • doqs8936 doqs8936 2018-04-07 22:17

    By adding (d datafield) onto the function signature you are making the datafield a receiver argument (https://tour.golang.org/methods/1) so the NewDatafield is now a method on datafield. This means to call it you need to already have a datafield which didn't make much sense so you could just remove the (d datafield) and it will work or even better just construct the stuct directly (https://gobyexample.com/structs):

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

    You only really need constructor functions when there is extra work to be done, not just setting fields.

    点赞 评论 复制链接分享
  • dongsu3664 dongsu3664 2018-04-08 04:33

    Go isn't exactly object oriented language and promotes simplicity, that's why the previous answers are focused on fixing your code. Nevertheless, if you really need to implement this design pattern in Go, read further.

    The factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method. (wikipedia)

    Here is another definition and a really good example in Go by Svetlin Ralchev:

    The Factory Method pattern is used to define a runtime interface for creating an object. It’s called a factory because it creates various types of objects without necessarily knowing what kind of object it creates or how to create it. (Desing Patterns in Golang: Factory Method)

    I've extended your example a bit to demonstrate the benefits of using Factory Methods, as there is no need to use factories at all if you're dealing with one struct (an object in OO world). 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()
    }
    

    The program simply prints this

    dataField1 -> name1 : value1
    dataField2 -> name2 : value2
    

    However, if you look into main func you don't find any mentions or initializations of concrete types dataField1 and dataField2. All the complexity is hidden behind dataFieldFactories. Both dataField1Factory and dataField2Factory implement Create interface and return dataField interface, that both the concrete types also implement. So, you can call Print() for each of the concrete types.

    Document.Print() uses both the Create and Print interfaces for printing out all the fields without any knowledge about how the fields are actually created or printed. We're achieving this by providing a list of Factory Methods (dataField1Factory{} and dataField2Factory{}) and corresponding string values to the Document struct (object).

    Excuse me for a bit artificial example, but I hope, you'll get the basic idea. As you can see Go allows you to implement design patterns you're familiar with, perhaps, not in exact same way as you get used to in pure OO languages.

    点赞 评论 复制链接分享

相关推荐