Подтвердить что ты не робот

Перейти: "Статический" метод проектирования

Я ищу совет по наилучшему способу очистки следующей структуры. Я знаю, что Go не имеет статических методов, и обычно лучше инкапсулировать функциональность в отдельном пакете. Мои типы структур ссылаются друг на друга и поэтому не могут быть объявлены в отдельных пакетах из-за циклического импорта.

type Payment struct {
    User *User
}

type User struct {
    Payments *[]Payments
}

func (u *User) Get(id int) *User {
    // Returns the user with the given id 
}

func (p *Payment) Get(id int) *Payment {
    // Returns the payment with the given id 
}

Но, если я хочу загрузить пользователя или платеж, я просто отбрасываю получателя:

var u *User
user := u.Get(585)

Я мог бы пропустить пробелы в самих функциях, которые ставят меня нечистым:

func GetUser(id int) *User {
    // Returns the user with the given id 
}

func GetPayment(id int) *Payment {
    // Returns the payment with the given id 
}

Мне бы очень хотелось просто вызвать .Get или подобное в структуре без написания имени структуры в самой функции. Какой идиоматический способ сделать это?

4b9b3361

Ответ 1

GetUser() и GetPayment() поражают меня как совершенно ясные и идиоматические. Я не уверен, что вы считаете нечистым о них.

Вызов .Get() в структуре для возврата другой структуры - это то, что поражает меня как очень странное, нечеткое и унииоматическое.

Я думаю, что это может быть случай просто придерживаться идиомы и доверять тому, что вы привыкнете к ней.

Ответ 2

с функцией Get отлично работает; это не унииоматично.

func (u *User) Get(id int) *User не имеет никакого смысла, но должно быть func (u *User) Get(id int) error. Единственное, чего вам не хватает, это то, что вы можете определить приемник метода на указателе, а затем внутри этого метода разыщите указатель, чтобы перезаписать то, на что он указывает.

Вот так:

// Returns the user with the given id 
func (u *User) Get(id int) error {
    *u = User{ ... } // dereference the pointer and assign something to it
    return nil // or an error here
}

и если возникла какая-либо проблема, верните ошибку. Теперь вы можете сказать

type Getter interface {
    Get(int) error
}

и поэтому может быть определен любой тип, который определяет Get(id)error. Затем вы будете использовать его следующим образом:

u := new(User)
if err := u.Get(id); err != nil {
    // problem getting user
}
// everything is cool.

Ответ 3

Golang не поддерживает конструкторы.

Вместо этого используйте factory функции (Эффективная ссылка Go). Соглашение заключается в использовании префикса New:

func NewUser(id int) *User {
    // Returns new User instance
}

Разница между конструктором и функцией factory заключается в том, что функция factory не привязана к структуре User. Это нормальная функция, которая возвращает User, в то время как конструктор типа Java/С++ - это метод, который изменяет только что созданный объект User.

Ответ 4

Другой способ вызова имитации статического метода, несмотря на то, что это не так, заключается в следующем:

package main

import "fmt"

type Manager struct {
}

func (m Manager) MyMethod(a float32, b float32) float32 {
    return 0.5 * a * b
}

func main() {
    fmt.Println((Manager).MyMethod(Manager{}, 15, 25))
}

Но, с моей точки зрения, это менее понятно, чем помещать этот метод в отдельный пакет manager снаружи от класса Manager