OOP
Go语言不是面向对象的语言, 而是通过一些方法来模拟 面向对象
类: 模板 -> 具体的实例 (对象) 分类 抽象 可以解决很多复杂的问题, 将现实的问题抽象~
这是一种思维, 抽象化 -> 模板
面向对象三大核心点
继承
继承即使子类继承父类的特征和行为, 使子类具有父类的属性和方法。子类具有父类的一般特性 也会具有自己的特性 父类更通用, 子类更具体
Go语言的嵌套: 结构体嵌套~
- 模拟继承
type A struct{
field
B
}
type B struct{
}
- 聚合关系
B 是无法直接访问A中的属性的 只能通过 B.a.a_field 来访问A中的属性 or 方法
type A struct {
a_ field
B
}
type B struct {
a A // 聚合关系
field
}
匿名字段 + 提升字段 -> Go 的匿名字段
package main
import "fmt"
// 定义一个父类
type Person struct {
name string
age int
}
// 定义一个子类, Student 拥有父类所拥有的属性, 还有自己的属性
type Student struct {
Person // 匿名字段, 实现继承
school string // 子类自己的属性
}
func main() {
// 创建父类对象
p1 := Person{name: "sam", age: 19}
fmt.Println(p1)
fmt.Println(p1.name, p1.age)
// 创建子类
s1 := Student{Person: Person{name: "Tom", age: 10}, school: "xiguan"}
fmt.Println(s1)
fmt.Println(s1.name, s1.Person)
// 创建子类(2)
var s2 Student // 定义变量~
s2.Person.name = "Son"
s2.Person.age = 19 // 赋值
s2.school = "china"
fmt.Println(s2)
// 提升字段: 只有匿名字段才能做到
// 如 Student 中 Person 是匿名字段, 故 Person中的属性 为提升字段
// 所有的提升字段可以直接使用, 不需再通过 .匿名字段 的形式进行访问
var s3 Student
s3.name = "John"
s3.age = 199
s3.school = "Hhh"
fmt.Println(s3)
fmt.Println(s3.name, s3.age)
}
方法
要属于某个结构体
方法和函数有不同, 要注意区分
方法:
- 需要指明调用者, 约定这个方法是属于谁的. 使用方法: 对象.方法()
- 多个类的方法可以重名, 但各类不行
- 方法是某个类的动作
函数:
- 不需要指定调用者, 定义了的函数就可以 使用 函数名() 进行调用
- 命名不能冲突
- 函数是一个特殊的类型
package main
import "fmt"
type Dog struct {
name string
age int
}
// 方法定义 需要指明调用者
func (d Dog) eat() {
fmt.Println(d.name + " is eating~")
}
func (d Dog) sleep() {
fmt.Println(d.name + " is sleeping~")
}
type Cat struct {
name string
age int
}
// 方法可重名, 只要调用者不一样即可
func (c Cat) eat() {
fmt.Println(c.name + " is eating ~")
}
func (c Cat) sleep() {
fmt.Println(c.name + " is sleeping ~")
}
func main() {
// 创建对象
dog := Dog{
name: "sam",
age: 2,
}
fmt.Println(dog) // 打印对象
// 方法的调用, 通过对应的 结构体.方法名() 即可
dog.eat()
dog.sleep()
// 猫对象也是类似使用方法~
cat := Cat{name: "Amy", age: 19}
cat.eat()
}
方法的重写
需要与继承一起使用
子类可以重写父类的方法, 父类不能访问子类的方法
package main
import "fmt"
// 方法重写 是建立在父类和子类的结构上
// 父类
type Animal struct {
name string
age int
}
func (anmial Animal) eat() {
fmt.Println(anmial.name, " is eating")
}
func (anmial Animal) sleep() {
fmt.Println(anmial.name, " is sleep")
}
// 子类
type Dog struct {
Animal
sex string
}
// 子类重写父类的方法, 方法名相同, 重写父类方法
func (d Dog) eat() {
fmt.Println("Name is", d.name, "it's", d.age, "years old",
"and it is eating something.")
}
func main() {
// 要么都以标签名:属性值的格式, 要么直接赋值(按顺序即可)
dog := Dog{Animal: Animal{name: "sam", age: 19}, sex: "male"}
dog.eat() // 子类重写了父类的方法 那么调用的时候是调用子类自己的方法
}
封装
使用 结构体+方法 来实现
就是封装细节, 只是暴露给用户我们提供的属性or方法 等功能~
接口
package main
import "fmt"
// 接口: Go 提供了接口数据类型
// 接口是把一些共性的方法 集合在一起去定义
// 若有实现类把接口定义的方法全部都实现了, 那么就代表实现了这个接口
// 代码追求的是: 高内聚, 低耦合 (类与类之间关联少)
// 接口时方法的定义集合, 不需要实现具体的方法内容
// 接口的定义
type USB interface { // 接口, 方法的结合
input()
output()
}
// 结构体
type Mouse struct {
name string
}
// 实现接口的全部方法就代表实现了这个接口
func (mouse Mouse) input() {
fmt.Println(mouse.name, "鼠标输入")
}
func (mouse Mouse) output() {
fmt.Println(mouse.name, "鼠标输出")
}
// 测试函数
func test(u USB) {
u.input()
u.output()
}
func main() {
m1 := Mouse{name: "Logic"}
test(m1) // 如果一个结构体实现了接口的所有方法, 那么这个结构体就是接口类型的
// 同理 可以用其他结构体去实现接口 从而达到多态的效果
// 定义高级类
var usb USB
usb = m1 // 类型会升级, mouse -> usb 向上转型
fmt.Println(usb) // 接口是无法使用 实现类的属性
}
多态
一个事物拥有多种形态, 如Anmial 包含了很多种动物, 如: Cat, Dog, Fish ...
类(模糊) -> 对象(具体) 向下转型
多态需要用 接口去模拟~
如上面的代码: Mouse有两重身份: 1. Mouse 2. USB -> 多态
接口的实现类都拥有多态特性: 除了自己本身还有对应接口的类型