Фабричный метод Голанга

avatar
SCote
7 апреля 2018 в 21:52
4403
3
2

Я изучаю 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) создать поле данных?

Источник
mkopriva
7 апреля 2018 в 23:23
0

В Go нет «методов класса», а также методы в Go — это просто функции, где получатель фактически является первым аргументом. (play.golang.org/p/RBJl0LZR5Bo)

Peter
8 апреля 2018 в 10:17
0

golang.org/ref/spec#Method_expressions

Ответы (3)

avatar
Philippe D.
7 апреля 2018 в 22:00
2

Вы не должны устанавливать метод для типа "поле данных" в вашем случае, вместо этого сделайте следующее:

func NewField(name, value string) *datafield {
    retval := new(datafield)
    retval.name = name
    retval.value = value
    return retval
}
SCote
8 апреля 2018 в 12:50
0

Это имеет смысл, основываясь на том, что написали другие. Это отвечает на мой вопрос, как задано.

vitr
8 апреля 2018 в 13:25
2

круто, также рассмотрите этот идиоматический oneliner field := &datafield{"name", "value"} полный код здесь goplay.space/#Acph3gPwrj2

avatar
vitr
8 апреля 2018 в 04:33
9

Go не совсем объектно-ориентированный язык и способствует простоте, поэтому предыдущие ответы сосредоточены на исправлении вашего кода. Тем не менее, если вам действительно нужно реализовать этот шаблон проектирования в Go, читайте дальше.

Шаблон фабричный метод — это порождающий шаблон, использующий фабрику методы решения проблемы создания объектов без необходимости указать точный класс объекта, который будет создан. Это делается путем создания объектов путем вызова фабричного метода. (википедия)

Вот еще одно определение и действительно хороший пример в Go от Светлина. Ральчев:

Шаблон Factory Method используется для определения интерфейса времени выполнения для создание объекта. Он называется фабрикой, потому что создает различные типы объектов, не обязательно зная, что это за объект создает или как его создать. (Шаблоны проектирования в Golang: Factory Метод)

Я немного расширил ваш пример, чтобы продемонстрировать преимущества использования фабричных методов, поскольку нет необходимости использовать фабрики вообще, если вы имеете дело с одной структурой (объектом в мире объектно-ориентированного программирования). 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

Однако, если вы заглянете в основную функцию, вы не найдете никаких упоминаний или инициализаций конкретных типов dataField1 и dataField2. Вся сложность скрыта за dataFieldFactories. Оба dataField1Factory и dataField2Factory реализуют интерфейс Create и возвращают интерфейс dataField, который оба конкретных типа также реализовать. Таким образом, вы можете вызывать Print() для каждого из конкретных типов.

Document.Print() использует для печати интерфейсы Create и Print. из всех полей без каких-либо знаний о том, как поля на самом деле создаются или печатаются. Мы достигаем этого, предоставляя список фабричных методов (dataField1Factory{} и dataField2Factory{}) и соответствующие строковые значения для структуры документа (объекта).

Извините за немного искусственный пример, но я надеюсь, что вы уловили основную идею. Как видите, Go позволяет вам реализовывать шаблоны проектирования, с которыми вы знакомы, возможно, не совсем так, как вы привыкли в чисто объектно-ориентированных языках.

SCote
8 апреля 2018 в 12:58
1

Это было безумно полезное объяснение. Спасибо за расширение этого для меня, так как интерфейсы были следующими в моем списке, и это дает мне очень важный пример.

avatar
Iain Duncan
7 апреля 2018 в 22:17
3

Добавляя (d datafield) в сигнатуру функции, вы делаете поле данных аргументом получателя (https://tour.golang.org/methods/1), так что NewDatafield теперь является методом на поле данных. Это означает, что для его вызова у вас уже должно быть поле данных, которое не имеет особого смысла, поэтому вы можете просто удалить (d поле данных), и оно будет работать, или, что еще лучше, просто создать структуру напрямую (https://gobyexample .com/structs):

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

Функции конструктора действительно нужны только тогда, когда нужно выполнить дополнительную работу, а не просто задать поля.

SCote
8 апреля 2018 в 12:41
0

Понял. Когда есть экземпляр получателя, эта функция может быть вызвана через этот получатель. Как вы объяснили, при создании структуры идиома «конструктор» не требуется.

SCote
8 апреля 2018 в 12:50
0

Хотя это прояснило мою голову, я выберу ответ Филиппа Д., потому что технически он дал ответ на мой вопрос. Мой код, вероятно, будет отражать ваш подход.