“Static”方法设计

huangapple go评论88阅读模式
英文:

"Static" method design

问题

我正在寻求关于清理以下结构的最佳方法的建议。我知道Go语言没有静态方法,通常最好将功能封装在单独的包中。我的结构类型相互引用,因此由于循环导入的原因不能在单独的包中声明。

type Payment struct {
    User *User
}

type User struct {
    Payments *[]Payments
}

func (u *User) Get(id int) *User {
    // 返回具有给定id的用户
}

func (p *Payment) Get(id int) *Payment {
    // 返回具有给定id的支付
}

但是,如果我想加载用户或支付,我只是丢弃了接收器:

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

我可以给函数本身加上命名空间,但这让我觉得不够干净:

func GetUser(id int) *User {
    // 返回具有给定id的用户
}

func GetPayment(id int) *Payment {
    // 返回具有给定id的支付
}

我真的希望能够在结构体上调用.Get或类似的方法,而不必在函数本身中写出结构体的名称。有什么惯用的方法可以做到这一点吗?

英文:

I'm looking for advice on the best way to clean up the following structure. I know Go doesn't have static methods and it's usually better to encapsulate functionality in a separate package. My struct types reference each other, and so cannot be declared in separate packages because of circular imports.

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 
}

But, if I want to load a user or payment, I'm just throwing away the receiver:

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

I could namespace the functions themselves, which strikes me as unclean:

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

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

I would really like to be able to just call .Get or similar on the struct without writing the name of the struct in the function itself. What's the idiomatic way to do this?

答案1

得分: 35

GetUser()GetPayment()对我来说非常清晰和习惯用语。我不确定你认为它们有什么不清晰的地方。

在结构体上调用.Get()来返回另一个结构体,这是我觉得非常奇怪、不清晰和不习惯用语的地方。

我认为这可能是坚持惯用语的情况,相信你会逐渐习惯的。

英文:

GetUser() and GetPayment() strike me as perfectly clear and idiomatic. I'm not sure what you find unclean about them.

Calling .Get() on a struct to return another struct is the thing that strikes me as very odd, unclear, and unidiomatic.

I think this might be a case of just sticking with the idiom and trusting that you'll get used to it.

答案2

得分: 26

Golang不支持构造函数。

可以使用工厂函数代替(参考Effective Go文档)。惯例是使用New前缀:

func NewUser(id int) *User {
    // 返回新的User实例
}

构造函数和工厂函数的区别在于,工厂函数不与User结构体"绑定"。它只是一个普通函数,恰好返回User类型,而类似Java/C++的构造函数是一个在创建的User对象上进行修改的方法。

英文:

Golang does not support constructors.

Use factory functions instead (Effective Go reference). The convention is to use New prefix:

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

The difference between constructor and factory function is that factory function is not "attached" to the User struct. It's a normal function that happen to return User while the Java/C++ like constructor is a method that modifies newly created User object in place.

答案3

得分: 17

拥有一个Get函数是完全可以的;它在任何方面都不是不符合惯例的。

func (u *User) Get(id int) *User 没有任何意义,应该是 func (u *User) Get(id int) error。你所忽略的一件事是,你可以在指针上定义一个方法接收器,然后在该方法内部对指针进行解引用,以覆盖它所指向的内容。

像这样:

// 返回具有给定id的用户
func (u *User) Get(id int) error {
    *u = User{ ... } // 解引用指针并对其赋值
    return nil // 或者在这里返回一个错误
}

如果有任何问题,返回一个错误。现在你可以这样说:

type Getter interface {
    Get(int) error
}

因此,任何定义了 Get(id) error 的类型都可以被定义。然后你可以像这样使用它:

u := new(User)
if err := u.Get(id); err != nil {
    // 获取用户时出现问题
}
// 一切正常。
英文:

having a Get function is perfectly fine; it's not unidiomatic in any way.

func (u *User) Get(id int) *User doesn't make any sense, though, it should be func (u *User) Get(id int) error. The one thing that you're missing is that you can define a method receiver on a pointer, and then inside of that method, dereference the pointer to overwrite what it points to.

Like this:

// 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
}

and if there was any problem, return an error. Now you can say

type Getter interface {
    Get(int) error
}

and so any type that defines Get(id)error can be defined. You would then use it like this:

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

答案4

得分: 1

另一种调用模拟静态方法的方式,尽管它实际上并不是静态方法,如下所示:

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中会更容易理解。

根据评论,另一种选择是:

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

最后是使用指针的方式:

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))

    fmt.Println((&Manager{}).MyMethod(15, 25))
}
英文:

Another way to call simulating an static method, despite the fact it isn't, is as follows:

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

But from my point of view, it is less understandable than placing this method in a separate package manageroutside from class Manager

From comments, another alternative would be:

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

Finally with pointers:

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))

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

huangapple
  • 本文由 发表于 2013年9月8日 05:38:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/18678135.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定