go语言笔记(七)面向对象_继承
为什么需要继承
案例:
创建小学生结构体和大学生结构体,并实现一些基本方法,如查看信息、考试、填写成绩等。
package main
import "fmt"
//编写一个学生考试系统
//小学生
type Pupil struct {
Name string
Age int
Score int
}
//显示他的成绩
func (p *Pupil) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 成绩=%v\n", p.Name, p.Age, p.Score)
}
func (p *Pupil) SetScore(score int) {
//业务判断
p.Score = score
}
func (p *Pupil) testing() {
fmt.Println("小学生正在考试中.....")
}
//大学生
type Undergraduate struct {
Name string
Age int
Score int
}
//显示他的成绩
func (p *Undergraduate) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 成绩=%v\n", p.Name, p.Age, p.Score)
}
func (p *Undergraduate) SetScore(score int) {
//业务判断
p.Score = score
}
func (p *Undergraduate) testing() {
fmt.Println("大学生正在考试中.....")
}
//代码冗余.. 高中生....
func main() {
//测试
var pupil = &Pupil{
Name: "Tom",
Age: 10,
}
pupil.testing()
pupil.SetScore(90)
pupil.ShowInfo()
//测试
var graduate = &Undergraduate{
Name: "Mary",
Age: 20,
}
graduate.testing()
graduate.SetScore(90)
graduate.ShowInfo()
}
以上案例中,创建了两种结构体类型:小学生类型和大学生类型。
我们发现两个结构体的基本属性一致,且共享了一些基础操作比如输入成绩以及ShowInfo方法等,但是各自的testing方法实现的内容却不相同。
以此类推,假如还要创建高中生变量、初中生变量等,我们需要重复定义他们的基本属性和操作,造成代码冗余。同时不利于功能扩展。
继承可以实现代码复用,让我们的编程更加靠近人类思维。当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体(比如Student),在该结构体中定义这些相同的属性和方法。其它的结构体不需要重新定义这些属性(字段)和方法,只需嵌套一个 Student 匿名结构体即可。
也就是说:在 Golang 中,如果一个 struct 嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性。
嵌套匿名结构体的语法
type Goods struct {
Name string
Price int
}
type Book struct {
Goods//这里就是嵌套匿名结构体 Goods
Writer string
}
使用继承来改进案例中的代码
package main
import "fmt"
type Student struct {
Name string
Age int
Score int
}
//显示他的成绩
func (s *Student) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 成绩=%v\n", s.Name, s.Age, s.Score)
}
func (s *Student) SetScore(score int) {
//业务判断
s.Score = score
}
type Pupil struct {
Student
}
func (p *Pupil) testing() {
fmt.Println("小学生在考试...")
}
type Undergraduate struct {
Student
}
func (u *Undergraduate) testing() {
fmt.Println("大学生在考试...")
}
func main() {
//当我们对结构体嵌入了匿名结构体使用方法会发生变化
pupil := &Pupil{}
pupil.Student.Name = "tom~"
pupil.Student.Age = 8
pupil.testing()
pupil.Student.SetScore(70)
pupil.Student.ShowInfo()
graduate := &Undergraduate{}
graduate.Student.Name = "mary~"
graduate.Student.Age = 28
graduate.testing()
graduate.Student.SetScore(90)
graduate.Student.ShowInfo()
}
通过以上改进,代码的复用性提高。且更加容易扩展和维护
继承的深入探讨
1.结构体可以使用嵌套匿名结构体的所有字段和方法,无论首字母大小写都可以使用。
2.匿名结构体字段访问和方法调用可以简化,如:
A.B.Name//B为匿名结构体
可以直接简化为
A.Name
其实现的原理在于编译器在查找Name字段时先在A中查找,如果A中没有就会在A嵌套的匿名结构体B中查找。
3.当结构体和匿名结构体含有相同名称的字段或者方法且采取简化调用时,采取就近原则,首先访问结构体本身的字段或方法。
4.当结构体中嵌套了两个匿名结构体,那么在调用相同名称字段或方法时就不能使用简化方法,否则会报错。(多重继承)*为了保证代码的简洁性,尽量不要使用多重继承
5.如果嵌套了有名结构体,这种模式称为组合。在访问组合的字段或方法时不能简写,得写上嵌套的结构体的变量名。
称的字段或者方法且采取简化调用时,采取就近原则,首先访问结构体本身的字段或方法。
4.当结构体中嵌套了两个匿名结构体,那么在调用相同名称字段或方法时就不能使用简化方法,否则会报错。(多重继承)*为了保证代码的简洁性,尽量不要使用多重继承
5.如果嵌套了有名结构体,这种模式称为组合。在访问组合的字段或方法时不能简写,得写上嵌套的结构体的变量名。