考虑以下类型:
type A struct {
...
}
func (a *A) Process() {
...
}
我想将process
类型的方法A
传递给另一个函数,并且能够访问A
的基础实例的内容。
我应该如何将方法传递给另一个函数?通过指针?它应该怎么称呼?
Process()
方法不会修改A
的实例,我在方法接收器上使用指针,因为结构非常大。我的问题背后的想法是避免在结构外声明函数Process()
并向其传递大量参数(而不是访问结构的成员)。
答案 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)
}
答案 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中通过引用传递,就像slices
和maps
。)
总体而言,当您希望将逻辑分解为更小的可管理(且更可测试)的部分时,通常使用更小,更易于管理的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
}
当我掌握现有的代码库时,这些是我开始实现的一些技巧,以帮助将应用程序与自身分离 - 并允许进行更多测试。