函数类型与单个方法接口

时间:2015-08-12 09:42:22

标签: go

如果一个接口只有一个方法,我应该使用一个函数类型吗?

以下是两种方法的示例:

type Delegate interface {                 | type Delegate func(x int) int
  Do(x int) int                           | 
}                                         | 
                                          | 
type App struct {                         | type App struct {
  delegate Delegate                       |   delegate Delegate
}                                         | }
                                          | 
func (this *App) foo() {                  | func (this *App) foo() {
  ...                                     |   ...
  y := this.delegate.Do(x)                |   y := this.delegate(x)
  ...                                     |   ...
}                                         | }
                                          | 
func main() {                             | func main() {
  delegate := &DelegateImpl{}             |   delegate := &DelegateImpl{}
  app := &App{delegate}                   |   app := &App{delegate.Process}
  ...                                     |   ...
}                                         | }

试验:

type FakeDelegate {                       | 
  t *testing.T                            | 
  expectedX int                           | 
  result int                              | 
}                                         | 
                                          | 
func (this *FakeDelegate) Do(x int) int { | 
  require.Equal(t, this.expectedX, x)     | 
  return this.result                      | 
}                                         | 
                                          | 
func TestAppFoo(t *testing.T) {           | func TestAppFoo(t *testing.T) {
  app := &App{}                           |   app := &App{}
  app.delegate = &FakeDelegate{t, 1, 2}   |   app.delegate = func(x int) int {
  app.foo()                               |     require.Equal(t, 1, x)
}                                         |     return 2
                                          |   }
                                          |   app.foo()
                                          | }

看起来右边的代码(带有函数类型)的行数较少,特别是在测试时。但是,有没有陷阱?

2 个答案:

答案 0 :(得分:8)

在一般情况下

这是一个设计问题。接口提供的功能不是动态调度。因此,如果您以后希望(可能是您自己的)客户端代码将所述函数应用于对象,并设想该对象可能是程序中某个给定点的几种不同类型之一,那么请选择一个接口。

专业人士:你可以灵活变通。

缺点:

  • 动态调度在执行时间上花费很小的开销。例如,你不会在关键循环的中间想要它。
  • 接口(如果可用)将在系统增长时使用,可能会略微出乎意料。这意味着,虽然大多数时候你可以轻松地定义一个功能的责任范围,但作为设计决策,界面的责任和类型签名应该经过深思熟虑。
  • 对于试图理解代码的读者而言,还有更多的知识分子。

Go被设计为一种简单实用的语言。我认为,作为有意愿的用户,我们应该发扬其理念。因此,我要说:如果你不需要某些东西,就不要使用它。 :)

在您的特定情况下

尽管函数指针确实是开发人员管理的动态调度的一种形式,但在我看来,上述推理仍然适用:寻找可满足可预见需求的最简单的解决方案。否则Go将成为Java。如果您确定代理人永远不需要提供任何其他入口点(例如提供元信息),我会说坚持使用函数指针。

答案 1 :(得分:3)

我认为使用以下简单规则是合理的:

  • 如果最可能的实现适用于未在参数中传递的数据,则使用接口。标准库中的示例:io.Reader接口,使用单个方法Read
  • 否则使用功能。标准库中的示例:http.HandlerFuncbufio.SplitFunc

我也发现好名字有助于设计决策。