结构成员的Golang func指针

时间:2016-04-01 02:20:59

标签: pointers methods go

考虑以下类型:

type A struct {
    ...
}

func (a *A) Process() {
    ...
}

我想将process类型的方法A传递给另一个函数,并且能够访问A的基础实例的内容。

我应该如何将方法传递给另一个函数?通过指针?它应该怎么称呼?

Process()方法不会修改A的实例,我在方法接收器上使用指针,因为结构非常大。我的问题背后的想法是避免在结构外声明函数Process()并向其传递大量参数(而不是访问结构的成员)。

3 个答案:

答案 0 :(得分:6)

你甚至可以直接这样做,没有界面:

select MAX(cast(`text`as unsigned)) from `table`;

输出:

package main

import "fmt"

type A struct {
    Name string
}

func (a *A) Go() {
    fmt.Printf("GO: %v\n", a)
}

func Test(fn func()) {
    fn()
}

func main() {
    aa := &A{Name: "FOO"}
    bb := (*A)(nil)
    cc := &A{}
    Test(aa.Go)
    Test(bb.Go)
    Test(cc.Go)
}

在操场上:https://play.golang.org/p/V-q2_zwX8h

答案 1 :(得分:3)

您可以通过界面实现此目的:

type Processor interface {
    Process()
}

func anotherFunction(p Processor) {
    p.Process()
}

...
var a A
anotherFunction(a)

答案 2 :(得分:3)

另一种选择是将func定义为类型:

type Process func(a *A)

然后在调用其他函数时将其用作参数:

func Test(p Process)

要记住的一件事是这些定义完全相同:

  • func (a *A) Process() { a.MyVar }
  • func Process(a *A) { a.MyVar }

指针接收器只是一个带有指向struct的指针的局部变量的函数。相反,值接收器是一个带有局部变量的函数,该变量是结构的值副本。

为什么不在结构上使用方法,比如选项1?原因很多。将相关方法分组到结构本身(如选项1)似乎是直观的(特别是如果来自其他OOP语言,如Java或.NET,您通常会在单个结构上粘贴上千种方法)。但是,既然你说struct本身很大,那就是SoC(它太大了)的味道,可能需要分解。

就个人而言,我在使用上述选项2时遵循的规则是:

  • 如果func没有使用整个struct的属性(例如它只在一个数据子集上运行,甚至根本没有运行),我改为使用带有指针的选项2。 (或者,使用具有零字节结构的接口本身)

通过分解我所说的“非常大”的结构,可以更轻松地进行单元测试,这样我就可以只模拟支持该方法所需的功能接口。

现在,根据定义,函数定义本身就是类型。到目前为止,我们有这种类型:

func(a *A)

这可以作为您要求的另一种方法的输入,如下所示:

func AnotherFunc(fn func(a *A)) {
  a := &A{}
  fn(a)
}

但是对我来说,这让事情变得有点难以阅读,更不用说脆弱了 - 有人可以改变那里的func定义并在其他地方打破其他东西。

这是我更喜欢定义类型的地方:

type Process func(a *A)

这样,我可以像以下一样消费它:

func AnotherFunc(p Process) {
  a := &A{}
  p(a)
}

这允许您访问p作为指向func的指针,随意传递。 (注意,您不必访问p的实际指针.IOW,不要执行此操作&p因为func类型在Golang中通过引用传递,就像slicesmaps。)

总体而言,当您希望将逻辑分解为更小的可管理(且更可测试)的部分时,通常使用更小,更易于管理的AnotherFunc()方法来导出和单元测试API契约时,您通常会遵循这种模式因为,虽然隐藏内部。

工作示例

http://play.golang.org/p/RAJ2t0nWEc

package main

import "fmt"

type Process func(a *A)

type A struct {
  MyVar string
}

func processA(a *A) {
  fmt.Println(a.MyVar)
}

func AnotherFunc(a *A, p Process) {
  p(a)
}

func main() {

    a := &A{
        MyVar: "I'm here!",
    }

    AnotherFunc(a, processA)
}

单元测试

将func类型的概念提升到另一个层次,就是简化单元测试。

您可以为Process()函数定义全局变量:

var Process = func(a *A)

它将继续以完全相同的方式使用:

 func Test(p Process)

现在的区别在于单元测试,你可以覆盖函数:

package mypackage_test

import "testing"

func TestProcessHasError(t *testing.T) {
    // keep a backup copy of original definition
    originalFunctionality := Process

    // now, override it
    Process = func(a *A) error {
        // do something different here, like return error
        return errors.New("force it to error")
    }

    // call your Test func normally, using it
    err := Test(Process)

    // check for error
    assert.Error(err)

    // be a good tester and restore Process back to normal
    Process = originalFunctionality 
}

当我掌握现有的代码库时,这些是我开始实现的一些技巧,以帮助将应用程序与自身分离 - 并允许进行更多测试。