设计Go包:什么时候我应该在类型上定义方法?

时间:2014-09-02 21:12:41

标签: go

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

我应该使用什么抽象和什么时候?

  • 在该类型上定义方法

     func (T t) someLogic() {
     // ...
     } 
    
  • 定义功能

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

3 个答案:

答案 0 :(得分:5)

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

  • 改变接收器:修改对象字段的方法通常是方法。对于x.Foo修改X而不是Foo(x)的用户来说,这并不奇怪。
  • 通过接收器产生的副作用:如果某种类型的方法通过更微妙的方式对对象产生副作用,那么事物通常就是类型上的方法,例如写入{{1的一部分的网络连接或者通过结构中的指针或切片等来编写。
  • 访问私有字段:理论上,同一个包中的任何内容都可以看到对象的未导出字段,但更常见的是,只是对象的构造函数和方法。让其他东西查看未导出的字段有点像使用C ++ struct s。
  • 需要满足接口:只有方法可以成为接口的一部分,因此您可能需要创建一个方法来满足接口。例如,Peter Bourgon's Go introfriend定义为带有方法而不是函数的空结构,只是为了满足与不是type openWeatherMap接口。 em>空结构。
    • 测试存根:作为上述情况的一个特例,有时接口有助于stub out objects for testing,因此即使没有状态,您的存根实现也可能必须是方法。

您倾向于使用功能的地方:

  • 构造函数: weatherProvider是一个函数,而不是一个方法。 Go没有构造函数的概念,因此它必须如此。
  • 在接口或基本类型上运行:您无法在func NewFoo(...) (*Foo)或基本类型上添加方法(除非您使用interface使其成为新类型)。因此,typestrings.Split必须是函数。此外,reflect.DeepEqual必须是一个函数,因为它不能只在io.CopyReader上定义方法。请注意,这些并未声明新类型(例如,Writer)以避免无法对基本类型执行方法。
  • 从超大类型或包中移出功能:有时,单个类型(在某些Web应用中认为strings.MyStringUser)会积累大量功能,并且会造成伤害可读性或组织甚至导致结构性问题(如果变得难以避免cyclic imports)。使突变接收器,访问未导出的字段等的方法中的非方法可能是将其代码“向上”移动到应用程序的更高层的重构步骤或“over”到另一个类型/包,或者独立功能只是它最自然的长期地方。 (帽子提示Steve Francia包括hugo中关于他的Go错误的一个例子。)
  • 方便“只使用默认值”功能:如果您的用户可能希望快速使用“默认”对象值而无需显式创建对象,则可以公开执行此操作的函数,通常使用与对象方法相同的名称。例如,Page是一个包级别的函数,可以使http.ListenAndServe()变得微不足道,并在其上调用http.Server
  • 传递行为的函数:有时您不需要定义类型和接口只是为了传递功能而且只需要一个裸函数就足够了,如ListenAndServe或{{ 1}}或用于注册http.HandleFunc()支票等。不要强迫它。
  • 强制面向对象时的功能:如果他们呼叫某些帮助者,或者您的私人功能不合适,请说明template.Funcs()go vet更干净看看任何对象字段,永远不会。再说一遍,如果在你的情况下,你没有获得任何东西,不要觉得你必须强迫OO(àlamain())。

如果有疑问,如果某些内容是您导出的API的一部分,并且可以自然选择将其附加到哪种类型,请将其作为方法。但是,不要扭曲你的设计(将问题拉入你的类型或包装中可能是分开的),这样就可以成为一种方法。 init()type Application struct{...};如果他们这样做的话,很难实现。相反,您通过其他地方Writer添加了WriteJSON的JSON功能。

如果您仍然不确定,请首先编写以便文档清晰显示,然后使代码自然地读取(Writerjson.NewEncoder(w io.Writer)),然后选择感觉正确而不会出汗的情况很多,因为通常你可以稍后重新安排它。

答案 1 :(得分:3)

如果您正在操纵对象的内部secrets

,请使用此方法
   (T *t) func someLogic() {
      t.mu.Lock()
      ...
   }

如果您使用对象的public interface

,请使用此功能
  func somelogic(T *t) {
      t.DoThis()
      t.DoThat()
     }

答案 2 :(得分:0)

如果要更改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。