Golang的面向对象编程
版权声明:原创作品,谢绝转载!否则将追究法律责任。
Go语言的面向对象之所以与C++,Java以及(较小程度上的)Python这些语言不同,是因为它不支持继承,仅支持聚合(也叫组合)和嵌入。接下来我们一起来学习一下Go语言的面向对象编程吧。
一.面向对象编程思想
面向对象编程刚流行的时候,继承是它首先被吹捧的最大优点之一。但是历经几十载的实践之后,事实证明该特性也有些明显的缺点,特别是当用于维护大系统时。Go语言建议的是面向接口编程。 常见的编程方式: 面向过程(面向函数式编程): 典型代表: C语言 优点: 流程清晰,代码易读。 缺点: 耦合度太高,不利于项目迭代。 面向对象编程: 典型代表: C++,Java,Python,Golang等。 优点: 解耦。 缺点: 代码抽象度过高,不易读。 面向对象三要素: 封装 组装:将数据和操作组装到一起。 隐藏数据:对外只暴露一些接口,通过接口访问对象。比如驾驶员使用汽车,不需要了解汽车的构造细节,只需要知道使用什么部件怎么驾驶就行,踩了油门就能跑,可以不了解其中的机动原理。 继承 多复用,继承来的就不用自己写了。 多继承少修改,OCP(Open-closed Principle),使用继承来改变,来体现个性。 多态 面向对象编程最灵活的地方,动态绑定。 与其它大部分使用聚合和继承的面向对象语言不同的是,Go语言只支持聚合(也叫做组合)和嵌入。
二.结构体的定义及初始化
package main
import (
"fmt"
)
type Person struct {
Name string
Age int
Gender string
}
type Student struct {
Person //通过匿名组合的方式嵌入了Person的属性。
Score float64
}
type Teacher struct {
Person //通过匿名组合的方式嵌入了Person的属性。
Course string
}
type Schoolmaster struct {
Person //通过匿名组合的方式嵌入了Person的属性。
CarBrand string
}
func main() {
/**
第一种初始化方式:先定义后赋值
*/
s1 := Student{}
s1.Name = "Jason Yin"
fmt.Println(s1)
fmt.Printf("%+v
", s1) //"+v表示打印结构体的各个字段"
/**
第二种初始化方式:直接初始化
*/
s2 := Teacher{Person{"尹正杰", 18, "boy"}, "Go并发编程"}
fmt.Println(s2)
fmt.Printf("%+v
", s2)
/**
第三种赋值方式:初始化赋值部分字段
*/
s3 := Schoolmaster{CarBrand: "丰田", Person: Person{Name: "JasonYin最强王者"}}
fmt.Println(s3)
fmt.Printf("%+v
", s3)
}

三.结构体的属性继承及变量赋值
package main
import (
"fmt"
)
type Animal struct {
Age int
}
type People struct {
Animal
Name string
Age int
Gender string
}
type IdentityCard struct {
IdCardNO int
Nationality string
Address string
Age int
}
/*
此时的Students以及是多重继承
*/
type Students struct {
IdentityCard
People //多层继承
Age int
Score int
}
func main() {
/**
如果子类和父类存在同名的属性,那么以就近原则为准
*/
s1 := Students{
Score: 150,
IdentityCard: IdentityCard{
IdCardNO: 110105199003072872,
Nationality: "中华人民共和国",
Address: "北京市朝阳区望京SOHO",
Age: 8,
},
People: People{Name: "Jason Yin", Age: 18, Animal: Animal{Age: 20}},
Age: 27,
}
/**
如果子类和父类存在同名的属性(如果父类还继承了其它类型,我们称之为多层继承),那么就以就近原则为准;
但是如果一个子类如果继承自多个父类(我们称之为多重继承),且每个字段中都有相同的字段,此时我们无法直接在子类调用该属性;
*/
fmt.Printf("学生的年龄是:[%d]
", s1.Age)
s1.Age = 21
fmt.Printf("学生的年龄是:[%d]
", s1.Age)
//给People类的Age赋值
fmt.Printf("People的年龄是:[%d]
", s1.People.Age)
s1.People.Age = 5000
fmt.Printf("People的年龄是:[%d]
", s1.People.Age)
//给IdentityCard类的Age赋值
fmt.Printf("IdentityCard的年龄是:[%d]
", s1.IdentityCard.Age)
s1.IdentityCard.Age = 80
fmt.Printf("IdentityCard的年龄是:[%d]
", s1.IdentityCard.Age)
}

四.匿名组合对象指针使用案例

五.结构体成员方法案例
package main
import (
"fmt"
)
//定义一个结构体
type Lecturer struct {
Name string
Age uint8
}
//我们为Lecturer结构体封装Init成员方法
func (l *Lecturer) Init() {
l.Name = "Jason Yin"
l.Age = 20
}
/**
我为Lecturer结构体起一个别名
我们可以为Instructor类型添加成员方法,
通过别名和成员方法为原有类型赋值新的操作
*/
type Instructor Lecturer
/**
温馨提示:
(1)我们为一个结构体创建成员方法时,如果成员方法有接收者,需要考虑以下两种情况:
1>.如果这个接收者是对象的时候,是值传递;
2>.如果这个接收者是对象指针,是引用传递;
(2)只要函数接收者不同,哪怕函数名称相同,也不算同一个函数哟;
(3)不管接收者变量名称是否相同,只要类型一致(包括对象和对象指针),那么我们就认为接收者是相同的,这时候不允许出现相同名称函数;
(4)给指针添加方法的时候,不允许给指针类型添加操作(因为Go语言中指针类型是只读的);
*/
func (i *Instructor) Init() {
i.Name = "尹正杰"
i.Age = 18
}
func main() {
var (
l Lecturer
i Instructor
)
//可以使用对象调用成员方法
i.Init()
fmt.Printf("%+v
", i)
//可以用对象指针调用成员方法
(&l).Init()
fmt.Printf("%+v
", l)
}

六.结构体的方法继承和重写
package main
import (
"fmt"
)
type Father struct {
Name string
Age int
}
func (f *Father) Init() {
f.Name = "成龙"
f.Age = 66
}
//定义父类的Eat成员方法
func (f *Father) Eat() {
fmt.Println("Jackie Chan is eating...")
}
//重写父类的Eat成员方法
func (s *Son) Eat() {
fmt.Println("FangZuming is eating...")
}
//我们让Son类继承Father父类
type Son struct {
Father //匿名组合能够继承父类的属性和方法
Score int
}
func main() {
var s Son
s.Init()
fmt.Printf("%+v
", s)
s.Eat()
s.Name = "房祖名"
s.Age = 38
s.Score = 100
fmt.Printf("%+v
", s)
}

七.方法值和方法表达式
package main
import (
"fmt"
)
/**
定义函数,函数的返回值是函数类型
*/
func CallBack(a int) func(b int) int {
return func(c int) int {
fmt.Println("调用了CallBack这个回调函数...")
return a + c
}
}
type BigData struct {
Name string
}
func (this *BigData) Init() {
this.Name = "Hadoop"
}
func (this *BigData) PrinfInfo() {
fmt.Printf("%v是大数据生态圈的基石。
", this.Name)
}
func (this BigData) SetInfoValue() {
fmt.Printf("SetInfoValue : %p,%v
", &this, this)
}
func (this *BigData) SetInfoPointer() {
fmt.Printf("SetInfoPointer : %p,%v
", this, this)
}
func main() {
/**
调用回调函数的返回值为函数类型
*/
result := CallBack(10)
fmt.Printf("result的类型是:[%T],result的值是:[%v]
", result, result)
res1 := result(20) //我们对返回的函数再次进行调用
fmt.Printf("res1的类型是:[%T],res1的值是:[%d]
", res1, res1)
var hadoop BigData
hadoop.Init() //调用hadoop的初始化方法
info := hadoop.PrinfInfo //我们可以声明一个函数变量info,我们称之为方法表达式
/**
我们对info函数变量进行调用,这样可以起到隐藏调用者hadoop对象的效果哟~(类似于回调函数的调用效果)
方法值可以隐藏调用者,我们称为隐式调用。
*/
info()
/**
方法表达式可以显示调用调用,必须传递方法调用者对象,在实际开发中很少使用这种方式,我们了解即可。
*/
elk := BigData{"Elastic Stack"}
fmt.Printf("main:%p,%v
", &elk, elk)
s1 := (*BigData).SetInfoPointer
s1(&elk) //显示把接收者传递过去
s2 := (BigData).SetInfoValue
s2(elk) //显示把接收者传递过去
}

八.面向接口编程(多态案例)
博主推荐阅读: https://www.cnblogs.com/yinzhengjie2020/p/12542435.html