Go 面向对象

88 阅读4分钟

OOP

Go语言不是面向对象的语言, 而是通过一些方法来模拟 面向对象

类: 模板 -> 具体的实例 (对象) 分类 抽象 可以解决很多复杂的问题, 将现实的问题抽象~

这是一种思维, 抽象化 -> 模板

面向对象三大核心点

继承

继承即使子类继承父类的特征和行为, 使子类具有父类的属性和方法。子类具有父类的一般特性 也会具有自己的特性 父类更通用, 子类更具体

Go语言的嵌套: 结构体嵌套~

  1. 模拟继承
type A struct{
	field
	B
}
type B struct{

}
  1. 聚合关系

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 -> 多态

接口的实现类都拥有多态特性: 除了自己本身还有对应接口的类型