【问题标题】:Designing Go packages: when I should define methods on types?设计 Go 包:什么时候应该定义类型的方法?
【发布时间】:2015-05-27 13:14:17
【问题描述】:

假设我有一个类型type T int,我想定义一个逻辑来操作这个类型。

我应该使用什么抽象以及何时使用?

  • 在该类型上定义一个方法

     func (T t) someLogic() {
     // ...
     } 
    
  • 定义一个函数

     func somelogic(T t) {
     // ...
     }
    

【问题讨论】:

  • 第二个版本(函数)没有返回值基本上是无意义的。否则:这取决于。
  • @Volker:这并不常见,但这种形式有时对指针成员、切片或映射很有用;例如func (p IntSlice) Sort()在排序包中

标签: go


【解决方案1】:

你倾向于使用方法的一些情况:

  • 改变接收者:修改对象字段的东西通常是方法。与Foo(x) 相比,x.Foo 将修改 X 对您的用户来说并不那么令人惊讶。
  • 通过接收者的副作用:如果事物以更微妙的方式对/通过对象产生副作用,则通常是类型上的方法,例如写入struct 的一部分的网络连接,或写入通过结构中的指针或切片等。
  • 访问私有字段:理论上,同一个包中的任何内容都可以看到对象的未导出字段,但更常见的是,只有对象的构造函数和方法可以。让其他东西查看未导出的字段有点像拥有 C++ friends。
  • 满足接口所必需的:只有方法才能成为接口的一部分,因此您可能需要将某些东西作为方法来满足接口。例如,Peter Bourgon's Go introtype openWeatherMap 定义为具有方法而不是函数的空结构,只是为了满足与不是空结构的其他实现相同的weatherProvider 接口。
    • 测试存根:作为上述的一个特例,有时接口会帮助stub out objects for testing,因此您的存根实现可能必须是方法,即使它们没有状态。

一些你倾向于使用函数的地方:

  • 构造函数: func NewFoo(...) (*Foo) 是一个函数,而不是一个方法。 Go 没有构造函数的概念,所以它必须是这样的。
  • 在接口或基本类型上运行:您不能在interfaces 或基本类型上添加方法(除非您使用type 使它们成为新类型)。所以,strings.Splitreflect.DeepEqual 必须是函数。此外,io.Copy 必须是一个函数,因为它不能只在ReaderWriter 上定义一个方法。请注意,这些不会声明新类型(例如,strings.MyString)来解决无法对基本类型执行方法的问题。
  • 将功能从过大的类型或包中移出:有时单个类型(想想某些 Web 应用中的 UserPage)会积累大量功能,这会损害可读性或组织性,甚至会导致结构性问题问题(例如,如果要避免cyclic imports 变得更加困难)。从改变接收器、访问未导出字段等的方法中创建非方法可能是将其代码“向上”移动到应用程序的更高层的重构步骤,或者“over”到另一个类型/包,或者独立功能只是它最自然的长期位置。 (帽子提示 Steve Francia 在谈论他的围棋错误时包含了来自 hugo 的示例。)
  • 方便的“只使用默认值”函数:如果您的用户可能想要一种快速使用“默认”对象值而不显式创建对象的方法,您可以公开执行此操作的函数,通常使用相同的名称作为对象方法。例如,http.ListenAndServe() 是一个包级函数,它生成一个微不足道的 http.Server 并在其上调用 ListenAndServe
  • 传递行为的函数: 有时您不需要定义类型和接口来传递功能,一个裸函数就足够了,如http.HandleFunc()template.Funcs() 或注册@ 987654348@支票等等。不要强求。
  • 强制面向对象的函数: 假设你的main()init() 更干净,如果它们调用了一些助手,或者你有不查看任何对象字段的私有函数并且永不。同样,如果在您的情况下,您没有从中获得任何好处,您不必强迫 OO (à la type Application struct{...})。

如果有疑问,如果某些内容是您导出的 API 的一部分,并且可以自然地选择将其附加到什么类型,请将其设为方法。但是,不要扭曲您的设计(将关注点放入您的类型或可能是单独的包中),以便某些东西可以成为一种方法。 Writers 不要WriteJSON;如果他们这样做了,就很难实施。相反,您可以通过其他地方的函数 json.NewEncoder(w io.Writer) 将 JSON 功能添加到 Writers。

如果您仍然不确定,请先编写以使文档阅读清晰,然后使代码自然阅读(o.Verb()o.Attrib()),然后按照感觉正确的方式去做,而不必费力太多,因为通常您可以稍后重新排列。

【讨论】:

    【解决方案2】:

    如果您正在操作对象的内部secrets,请使用该方法

       (T *t) func someLogic() {
          t.mu.Lock()
          ...
       }
    

    如果使用对象的public interface,请使用该函数

      func somelogic(T *t) {
          t.DoThis()
          t.DoThat()
         }
    

    【讨论】:

      【解决方案3】:

      如果您想更改 T 对象,请使用

       func (t *T) someLogic() {
       // ...
       } 
      

      如果您不更改 T 对象并希望使用原始对象方式,请使用

       func (t T) someLogic() {
       // ...
       }
      

      但请记住,这将生成一个临时对象 T 来调用 someLogic

      如果你喜欢c语言的方式,请使用

       func somelogic(t T) {
            t.DoThis()
            t.DoThat()
       }
      

         func somelogic(t T) {
            t.DoThis()
            t.DoThat()
           }
      

      还有一件事,类型在golang中的var后面。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-03-23
        • 2014-08-14
        • 2011-09-03
        • 1970-01-01
        • 2011-03-08
        • 2012-12-23
        相关资源
        最近更新 更多