ch17、面向对象编程 - 扩展与复用
1、扩展与复用(即结构体内嵌)
Go不支持继承,通过组合(内嵌)的方式来实现(没有override的特性,Go本身不支持隐式类型转换,对于内嵌来说显式转换也不行)
与其它主要编程语言的差异:
- 不支持子类替换
- 子类并不是真正继承了父类的方法(“duck type”)
- 父类定义的方法无法访问子类的数据和方法
内嵌:B嵌入A,则A具备B的所有字段和行为(方法)
type B struct{
X,Y int
}
type A struct{
B
Z int
}
// 则A具备有X,Y,Z字段
匿名嵌入结构体实例
package extension
import (
"fmt"
"testing"
)
type Pet struct {
}
func (p *Pet) Speak() {
fmt.Print("...")
}
func (p *Pet) SpeakTo(host string) {
p.Speak()
fmt.Println(" ", host)
}
type Dog struct {
Pet
}
func (d *Dog) Speak() {
fmt.Println("wang!")
}
func TestDog(t *testing.T) {
dog := new(Dog)
dog.Speak()
dog.SpeakTo("Chao")
}
/*
output:
wang!
... Chao
*/
嵌入的类型可以是指针,此时,字段和方法间接的来自于所指向的对象(匿名嵌入结构体实例指针):
package extension
import (
"fmt"
"testing"
)
type Pet struct {
}
func (p *Pet) Speak() {
fmt.Print("...")
}
func (p *Pet) SpeakTo(host string) {
p.Speak()
fmt.Println(" ", host)
}
type Cat struct {
*Pet
}
func (c *Cat) Speak() {
fmt.Println("miaomiao!")
}
func (c *Cat) toPet(p *Pet, s string) {
p.SpeakTo(s)
}
func TestCat(t *testing.T) {
cat := new(Cat)
cat.Speak()
cat.SpeakTo("Chao")
cat.toPet(&Pet{}, "cat miaomiao")
}
/*
output:
miaomiao!
... Chao
... cat miaomiao
*/
- 继承结构中出现重名情况, 采用就近原则
package main
import "fmt"
type Person struct {
name string // 属性重名
age int
}
type Student struct {
Person
name string // 属性重名
score int
}
func main() {
s := Student{Person{"zs", 18}, "ls", 99}
fmt.Println(s.Person.name) // zs
fmt.Println(s.name) // ls
//fmt.Println(s.Person.age)
fmt.Println(s.age) // 两种方式都能访问
fmt.Println(s.score)
}
2、Go嵌入式"继承"机制的局限
不支持Overriding Methods。
package main
import (
"fmt"
)
type Pet struct {
name string
}
type Dog struct {
Pet
Breed string
}
func (p *Pet) Play() {
fmt.Println(p.Speak())
}
func (p *Pet) Speak() string {
return fmt.Sprintf("my name is %v", p.name)
}
func (p *Pet) Name() string {
return p.name
}
func (d *Dog) Speak() string {
return fmt.Sprintf("%v and I am a %v", d.Pet.Speak(), d.Breed)
}
func main() {
d := Dog{Pet: Pet{name: "spot"}, Breed: "pointer"}
fmt.Println(d.Name())
fmt.Println(d.Speak())
d.Play() // 依然会调用pet的Speak,而不是dog的Speak,即嵌入的方法不会被
}
/*
output:
spot
my name is spot and I am a pointer
my name is spot
*/
不存在Subtyping(没有多态)。
在Java中,Dog继承自Pet,那么Dog类型就是Pet子类。这意味着在任何需要调用Pet类型的场景都可以使用Dog类型替换。这种关系称作多态性,但Go的结构体类型不存在这种机制。
package main
import (
"fmt"
)
type Pet struct {
speaker func() string
name string
}
type Dog struct {
Pet
Breed string
}
func NewPet(name string) *Pet {
p := &Pet{
name: name,
}
p.speaker = p.speak
return p
}
func (p *Pet) Play() {
fmt.Println(p.Speak())
}
func (p *Pet) Speak() string {
return p.speaker()
}
func (p *Pet) speak() string {
return fmt.Sprintf("my name is %v", p.name)
}
func (p *Pet) Name() string {
return p.name
}
func NewDog(name, breed string) *Dog {
d := &Dog{
Pet: Pet{name: name},
Breed: breed,
}
d.speaker = d.speak
return d
}
func (d *Dog) speak() string {
return fmt.Sprintf("%v and I am a %v", d.Pet.speak(), d.Breed)
}
func Play(p *Pet) {
p.Play()
}
func main() {
d := NewDog("spot", "pointer")
fmt.Println(d.Name())
fmt.Println(d.Speak())
Play(d) // cannot use d (type *Dog) as type *Pet in argument to Play
}
但是,可以通过接口类型中存在子类化来实现多态机制!
package main
import (
"fmt"
)
type Pet interface {
Name() string
Speak() string
Play()
}
type pet struct {
speaker func() string
name string
}
type Dog interface {
Pet
Breed() string
}
type dog struct {
pet
breed string
}
func NewPet(name string) *pet {
p := &pet{
name: name,
}
p.speaker = p.speak
return p
}
func (p *pet) Play() {
fmt.Println(p.Speak())
}
func (p *pet) Speak() string {
return p.speaker()
}
func (p *pet) speak() string {
return fmt.Sprintf("my name is %v", p.name)
}
func (p *pet) Name() string {
return p.name
}
func NewDog(name, breed string) *dog {
d := &dog{
pet: pet{name: name},
breed: breed,
}
d.speaker = d.speak
return d
}
func (d *dog) speak() string {
return fmt.Sprintf("%v and I am a %v", d.pet.speak(), d.breed)
}
func Play(p Pet) {
p.Play()
}
func main() {
d := NewDog("spot", "pointer")
fmt.Println(d.Name())
fmt.Println(d.Speak())
Play(d)
}
/*
output:
spot
my name is spot and I am a pointer
my name is spot and I am a pointer
*/
参考文档:
3、如何分析一个类
- 一般名词都是类(名词提炼法)
- 飞机发射两颗炮弹摧毁了8辆装甲车
飞机
炮弹
装甲车
- 隔壁老王在公车上牵着一条叼着热狗的草泥马
老王
热狗
草泥马