【问题标题】:"Static" method design“静态”方法设计
【发布时间】:2013-09-11 18:08:16
【问题描述】:

我正在寻找有关清理以下结构的最佳方法的建议。我知道 Go 没有静态方法,通常是 better to encapsulate functionality in a separate package。我的结构类型相互引用,因此不能在单独的包中声明,因为循环导入。

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 或类似名称,而无需在函数本身中写入结构的名称。这样做的惯用方法是什么?

【问题讨论】:

    标签: go


    【解决方案1】:

    GetUser()GetPayment() 给我的印象是非常清晰和惯用的。我不确定你觉得它们有什么不干净的地方。

    在结构上调用.Get() 以返回另一个结构,这让我觉得非常奇怪、不清楚和单调。

    我认为这可能只是坚持使用习语并相信你会习惯它的情况。

    【讨论】:

    • 是的,我发现.Get() 在我丢弃的更不干净的结构上。对我来说User.Get() 是最干净的;如果GetUser 是我能得到的最接近的近似值,我会接受它。
    • @ash 是的,我相信这是你能得到的最接近的,也是最惯用的 Go 方式。
    • 您不能使用 GetUser 和 GetPayment 创建框架和库
    【解决方案2】:

    Golang 不支持构造函数。

    改用工厂函数 (Effective Go reference)。约定是使用New前缀:

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

    构造函数和工厂函数的区别在于工厂函数没有“附加”到User 结构。这是一个正常的函数,碰巧返回User,而类似Java/C++的构造函数是一种修改新创建的User对象的方法。

    【讨论】:

      【解决方案3】:

      有一个Get 函数非常好;无论如何,它都不是unidiomatic

      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.
      

      【讨论】:

      • 我这个这个语义更接近Load的通常含义而不是Get.的含义
      【解决方案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))
      }
      

      但在我看来,这比将这个方法放在一个单独的包manageroutside 与类Manager 之外更难理解

      在 cmets 中,另一种选择是:

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

      【讨论】:

      • 附带说明,(Manager).MyMethod(Manager{}, 15, 25) 调用您的方法的方式过于冗长。写Manager{}.MyMethod(15, 25) 就足够了。尽管Manager{} 语义上意味着Manager 的新实例,但Golang 编译器对空结构体进行了特殊处理,它应该能够完全优化掉“对象创建”。请参阅here 了解我重写的代码。
      猜你喜欢
      • 2016-10-06
      • 2011-02-19
      • 2011-01-03
      • 1970-01-01
      • 1970-01-01
      • 2014-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多