首页 > Go 分布式学习利器(12)-- Go语言的扩展和复用

Go 分布式学习利器(12)-- Go语言的扩展和复用

Go语言无法天然支持继承,但是又想要实现面向对象的特性。

即父类对象 使用子类对象初始化,那么该父类对象调用的函数就是子类实现的函数 ,从而满足LSP(子类交换原则)。

案例一: Go语言 支持扩展父类的功能,如下代码:

package oriented_testimport ("fmt""testing"
)// Pet 类
type Pet struct { 
}func (p *Pet) Speak(){  // Pet类的函数成员fmt.Print("Pet speak.
")
}func (p *Pet) SpeakTo(host string) {  // Pet类的函数成员p.Speak()fmt.Println("Pet SpeakTo ", host)
}// Dog 扩展Pet的功能
type Dog struct { p *Pet
}// 扩展父类的方法
func (d *Dog) Speak(){ d.p.Speak()
}// 扩展父类的方法
func (d *Dog) SpeakTo(host string) { d.Speak()fmt.Println("Dog Speakto ", host)
}func TestDog(t *testing.T) { dog := new(Dog)dog.SpeakTo("Test dog")
}

以上测试代码的输出如下:

Pet speak.
Dog Speakto  Test dog

dog的SpeakTo中调用了dog 的Speak,其中调用了Pet的Speak,所以输出正常。

Pet 和 Dog调用不会相互影响,完全由用户决定。

但是这和我们所想需要的不同,Pet类有自己的方法,Dog类有自己的方法,两者作用域完全不同。

这里Go语言推出了匿名嵌套类型,即Dog类不用实现自己的和Pet类同名的方法即可,通过在Dog类的声明中变更Pet成员。

案例二: Go语言支持匿名函数类型

// Dog 扩展Pet的功能
type Dog struct { Pet
}

这样即不需要Dog声明自己的同名函数成员,默认的调用即为Pet成员函数的调用

package oriented_testimport ("fmt""testing"
)type Pet struct { 
}func (p *Pet) Speak(){ fmt.Print("Pet speak.
")
}func (p *Pet) SpeakTo(host string) { p.Speak()fmt.Println("Pet SpeakTo ", host)
}// Dog 扩展Pet的功能
type Dog struct { Pet // 支持匿名嵌套类型,
}func TestDog(t *testing.T) { var dog Dogdog.Speak()dog.SpeakTo("Test dog")
}

最终的输出如下:

=== RUN   TestDog
Pet speak.
Pet speak.
Pet SpeakTo  Test dog
--- PASS: TestDog (0.00s)

调用的都是Pet的成员函数,感觉像是继承了,因为继承默认就是子类能够使用父类的公有成员。

在匿名嵌套类型下,我们想要完整尝试一下Go语言是否真正支持继承,可以像之前的代码一样在Dog中实现Pet的同名函数,且能够通过父类对象调用子类的成员方法,像C++/Java这样进行向上类型转换(本身是不可能的,Go语言不支持显式类型转换)。

案例三: Go语言不支持继承,如下代码:

package oriented_testimport ("fmt""testing"
)type Pet struct { 
}func (p *Pet) Speak(){ fmt.Print("Pet speak.
")
}func (p *Pet) SpeakTo(host string) { p.Speak()fmt.Println("Pet SpeakTo ", host)
}// Dog 扩展Pet的功能
type Dog struct { //p *PetPet // 支持匿名嵌套类型
}// 重载父类的方法
func (d *Dog) Speak(){ fmt.Print("Dog speak.
")
}// 重载父类的方法
func (d *Dog) SpeakTo(host string) { d.Speak()fmt.Println("Dog Speakto ", host)
}func TestDog(t *testing.T) { var dog Pet = new(Dog)dog.Speak()dog.SpeakTo("Test dog")
}

在最后的输出会编译出错,不支持将Pet类型转换为Dog类型:

./oriented_test.go:38:6: cannot use new(Dog) (type *Dog) as type Pet in assignment

总结一下,Go语言并不支持继承,能够支持接口的扩展 和 复用(匿名嵌套类型)

其中扩展 就是不同类实现相同的成员函数,能够实现类似于案例一中的扩展接口形态。

复用则是通过匿名嵌套类型实现 类似于重载的功能,可以看看案例二的代码。

更多相关:

  • 师弟总结的已经很好了 或者: 原工程需要导出的函数: extern "C" __declspec(dllexport) void Func(); 现工程导入这个函数: extern "C" __declspec(dllimport) void Func();...

  • 有使用过JS的朋友,相信都知道function。JS中的function是可以在里面在定义一个作为内部使用的。有时为了控制作用域,或者这种小函数只在这个函数体内会使用,所以就不希望在外部在作额外的定义。那C#中有没有这样类似的方式呢?答案是有的。 在C#中要实现,需要用到的是委托和lambda表达式。对于lambda表达式,是可以实...