Go语言面向对象编程的三大特性
封装
什么是封装
把抽象出来的字段和对应字段的方法封装在一起,数据保护在内部,只对外提供接口
封装的好处
- 对外隐藏实现细节
- 可以对数据进行验证,保证安全合理
封装如何实现
- 将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似 private)
- 给结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数
- 提供一个首字母大写的 Set 方法(类似其它语言的 public),用于对属性判断并赋值 func (var 结构体类型名) SetXxx(参数列表) (返回值列表) { //加入数据验证的业务逻辑 var.字段 = 参数 }
- 提供一个首字母大写的 Get 方法(类似其它语言的 public),用于获取属性的值 func (var 结构体类型名) GetXxx() { return var.age; }
- 特别说明:在 Golang 开发中并没有特别强调封装,这点并不像 Java. 所以提醒学过 java 的朋友, 不用总是用 java 的语法特性来看待 Golang, Golang 本身对面向对象的特性做了简化的
继承
什么是继承?
当多个结构体存在相同的属性和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法
其他的结构体无需再重新定义这些属性和方法,只需嵌套一个匿名结构体即可。这个过程称为继承
继承可以干什么?有什么好处
通过继承可以实现代码复用
方便功能扩展
利于维护代码
实现继承的基本语法
type Goods struct {
Name string
Price float32
}
type Book struct {
Goods // 嵌套匿名结构体
.....
}
一个继承实例
package main
import "fmt"
// 定义一个结构体A
type A struct {
Name string
age int
}
// 属于结构体A的方法 首字母大写
func (a *A) SayOk() {
fmt.Println("A SaYOk", a.Name)
}
// 属于结构体的方法 首字母小写
func (a *A) hello() {
fmt.Println("A hello", a.Name)
}
// 定义一个结构体B,继承A
type B struct {
A // 匿名嵌套A
}
func main() {
var b B
b.A.Name = "tom"
b.A.age = 23
b.A.SayOk()
b.A.hello()
// 上面的方法可以简化可以简化
b.Name = "Jack"
b.age = 24
b.SayOk()
b.hello()
}
对上面的代码小结 (1) 当我们直接通过 b 访问字段或方法时,其执行流程如下比如 b.Name
(2) 编译器会先看 b 对应的类型有没有 Name, 如果有,则直接调用 B 类型的 Name 字段
(3) 如果没有就去看 B 中嵌入的匿名结构体 A 有没有声明 Name 字段,如果有就调用,如果没有 继续查找..如果都找不到就报错.
(4)当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问 匿名结构体的字段和方法,可以通过匿名结构体名来区分
(5)结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身 没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错
(6)如果一个 struct 嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合 的结构体的字段或方法时,必须带上结构体的名字
type Goods struct {
Name string
Price float32
}
type Book struct {
goods Goods// 有名结构体 ,也成组合关系
.....
}
(7)嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
多继承
如一个 struct 嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方 法,从而实现了多重继承。
多继承注意事项
- 如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来 区分。
- 为了保证代码的简洁性,建议大家尽量不使用多重继承
多态
什么是多态
变量(实例)具有很多形态。再Go语言中,多态是通过接口实现的,可以按照统一的接口调用不同的实现,这时接口变量就呈现不同的形态
多态的两种实现形式
-
多态参数(通过传入的参数实现多态)
package main import "fmt" type Usbs interface { Start() Stop() } type Phones struct { } // Phone 实现 Usb 接口的方法 func (p Phones) Start(){ fmt.Println("手机开始工作") } func (p Phones) Stop() { fmt.Println("手机停止工作") } type Cameras struct { } // 让Camera实现 Usb 的方法 func (cameras Cameras) Start() { fmt.Println("相机开始工作") } func (cameras Cameras) Stop() { fmt.Println("相机停止工作") } type Computer struct { } func (c Computer) Working(usbs Usbs) { // usb 变量会根据传入的实参,来判断到底是 Phone,还是 Camera //通过 usb 接口变量来调用 Start 和 Stop 方法 usbs.Start() usbs.Stop() } func main() { computer := Computer{} phones := Phones{} cameras := Cameras{} computer.Working(phones) computer.Working(cameras) } -
多态数组
package main
import "fmt"
// 定义一个接口
type Usb interface {
// 声明两个没有实现的方法
Start()
Stop()
}
type Phone struct {
name string
}
// 让Phone实现 Usb 接口的方法
func (phone Phone) Start() {
fmt.Println("手机开始工作")
}
func (phone Phone) Stop() {
fmt.Println("手机停止工作")
}
type Camera struct {
name string
}
// 让Camera实现 Usb 的方法
func (camera Camera) Start() {
fmt.Println("相机开始工作")
}
func (camera Camera) Stop() {
fmt.Println("相机停止工作")
}
func main() {
// 定义一个Usb接口数组,可以存放Phone和Camera的结构体变量
// 这里提醒出数组的多态
var usbArr [3]Usb
usbArr[0] = Phone{name: "小米"}
usbArr[1] = Phone{name: "vivo"}
usbArr[2] = Camera{name: "佳能"}
fmt.Println(usbArr)
usbArr[0].Start()
usbArr[2].Start()
}