给定以下类型:
1 2 3 4 5 6 7 | type A struct { ... } func (a *A) Process() { ... } |
我想将类型
我应该如何将方法传递给另一个函数? 通过指针? 怎么称呼它呢?
您甚至可以直接进行操作,而无需使用界面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package main import"fmt" type A struct { Name string } func (a *A) Go() { fmt.Printf("GO: %v\ ", 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) } |
输出:
1 2 3 | GO: &{FOO} GO: <nil> GO: &{} |
在操场上:https://play.golang.org/p/V-q2_zwX8h
另一个选择是将
1 | type Process func(a *A) |
然后在调用其他函数时将其用作参数:
1 | func Test(p Process) |
要记住的一件事是这些定义是完全一样的:
-
func (a *A) Process() { a.MyVar } -
func Process(a *A) { a.MyVar }
指针接收器只是一个带有指向该结构的指针的局部变量的函数。相反,值接收器是一个带有局部变量的函子,该局部变量对应于该结构的值副本。
为什么不使用结构上的方法,如选项1?原因有很多。像选项1一样,将相关方法分组到结构本身上似乎很直观(特别是如果来自其他OOP语言,如Java或.NET,通常在单个结构上粘贴上万个方法)。但是,由于您说
就个人而言,使用上述选项2时遵循的规则是:
-
如果
func 没有使用整个结构的属性(例如,它仅对数据的子集进行操作,甚至根本不操作),则我将选项2与指针一起使用。 (或者,将接口本身使用零字节结构)
通过分解您所说的"很大"的结构,这使单元测试更加容易,使我可以仅模拟支持该方法所需的功能接口。
现在,根据定义,func定义本身就是类型。到目前为止,我们有这种类型:
1 | func(a *A) |
可以将其用作您要求的另一种方法的输入,例如:
1 2 3 4 | func AnotherFunc(fn func(a *A)) { a := &A{} fn(a) } |
但是对我来说,这使事情很难阅读,更不用说它很脆了–有人可以在那里更改func定义,并破坏其他地方的东西。
这是我更喜欢定义类型的地方:
1 | type Process func(a *A) |
这样,我可以像这样消费它:
1 2 3 4 | func AnotherFunc(p Process) { a := &A{} p(a) } |
这样,您就可以访问
总体而言,当您想将逻辑分解成较小的可管理(和可测试性更高)的部分时,通常会遵循这种模式-通过使用较小,更易于管理的
工作实例
http://play.golang.org/p/RAJ2t0nWEc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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类型的概念提升到另一个层次,将是简化单元测试。
您可以为
1 | var Process = func(a *A) |
它将继续以完全相同的方式使用:
1 | func Test(p Process) |
现在的区别是在单元测试期间,您可以覆盖该功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 } |
当我接触到现有的代码库时,这些就是我开始实现的一些技巧,以帮助使应用程序与自身脱钩-并允许进行更多测试。
- 尽管它不能直接回答问题,但我感谢您付出的努力,也谢谢您,因为它非常有用且很有帮助。
您可以通过以下接口实现此目的:
1 2 3 4 5 6 7 8 9 10 11 | type Processor interface { Process() } func anotherFunction(p Processor) { p.Process() } ... var a A anotherFunction(a) |