Go 面向对象OOP思想

发布于:2024-05-18 ⋅ 阅读:(166) ⋅ 点赞:(0)

Go语言不是面向对象的语言,只是可以采用面向对象的思维通过一些方法来模拟面向对象。面向对象思维核心就三个点:封装、继承、多态

GO语言和传统的面向对象编程有所区别,并不是纯粹的面向对象语言。比如C++,Java的面向对象都是基于类的,但是Go没有类,Go是基于struct来实现OOP特性的。Go去掉了传统的OOP语言的方法重载、构造函数、析构函数等,但是Go仍然有着面向对象编程的继承、封装和多态的特性,只是实现方式比较不同。 

继承 

继承需要符合的关系是is-a,父类更通用,子类更具体

子类会具有父类的一般特性,也会具有自身的特性

继承就是子类继承父类的特征和行为,使得子类具有父类的属性和方法。

结构体嵌套 

package main

import "fmt"

// 定义一个父类
type Person struct {
   name string
   age  int
}

// 定义一个子类,Student拥有了父类所有的属性,还有自己的特性
type Student struct {
   Person // 匿名字段,实现了继承。
   school string
}

func main() {
   // 1、 创建父类对象
   p1 := Person{name: "Autumn", age: 18}
   fmt.Println(p1)
   fmt.Println(p1.name, p1.age)

   // 2、创建子类
   s1 := Student{Person: Person{name: "小明", age: 3}, school: "清华"}
   fmt.Println(s1)
   fmt.Println(s1.Person.name, s1.Person.age, s1.school)

   // 3、创建子类
   var s2 Student
   s2.Person.name = "张三"
   s2.Person.age = 3
   s2.school = "北大"
   fmt.Println(s2)

   // 概念:提升字段, 只有匿名字段才可以做到
   // Person 在Student中是一个匿名字段, Person中的属性 name age 就是提升字段
   // 所有的提升字段就可以直接使用了,不同在通过匿名字段来点了
   
   var s3 Student
   s3.name = "李四"
   s3.age = 4
   s3.school = "清华"
   fmt.Println(s3)
   fmt.Println(s3.name, s3.age)
}

        继承就是子类继承父类的特征和行为,使得子类具有父类的属性和方法,使得子类具有父类相同的行为。子类会具有父类的一般特性也会具有自身的特性。

方法&函数

Go语言中同时拥有函数和方法。一定要和其他只有方法的语言区分开 

方法:需要指定调用者,约定这个方法属于谁的.   对象.方法()

函数:不需要指定调用者,定义了函数就可以直接函数名()调用

  • 方法可以重名,只需要调用者不同

  • 如果调用者相同则不可重名

  • func (方法调用者)(方法名)(){}

package main

import "fmt"

// 方法:可以理解为函数多了一个调用者
// 方法可以重名,不同的对象,调用的结果是不一样的
type Dog struct {
	name string
	age  int
}

// 方法定义,   func 方法调用者  方法名()
// 1、方法可以重名,只需要调用者不同
// 2、如果调用者相同,则不能重名
func (dog Dog) eat() {
	fmt.Println("Dog eating...")
}

func (dog Dog) sleep() {
	fmt.Println("Dog sleep...")
}

type Cat struct {
	name string
	age  int
}

func (cat Cat) eat() {
	fmt.Println("Cat eating...")
}
func (cat Cat) sleep() {
	fmt.Println("Cat sleep...")
}

func main() {

	// 创建一个对象
	dog := Dog{
		name: "旺财",
		age:  2,
	}
	fmt.Println(dog)
	// 方法的调用,通过对应的结构体对象来调用
	dog.eat()

	cat := Cat{
		name: "喵喵",
		age:  1,
	}
	cat.eat()
}

 方法:

  • 某个类的行为功能,需要指定调用者

  • 一段独立的代码功能,必须要使用调用者来调用

  • 多个类的方法可以重名,单个类不行

  • 方法是某个类的动作

函数:

  • 一段独立的代码功能,可以直接调用

  • 命名完全不能冲突

  • 函数是一个特殊的类型

方法的重写

  • 子类可以重写父类的方法

  • 子类还可以有自己新增的方法

  • 子类可以访问父类中的属性和方法

package main

import (
	"fmt"
)

// 方法重写,建立在父类和子类结构上的
type Animal struct {
	name string
	age  int
}

func (animal Animal) eat() {
	fmt.Println(animal.name, " 正在eating...")
}
func (animal Animal) sleep() {
	fmt.Println(animal.name, " 正在sleeping....")
}

// 子类
type Dog struct {
	Animal
}

// 子类自己的方法
func (dog Dog) wang() {
	fmt.Println("wangwangwanwg~~~")
}

type Cat struct {
	Animal
	color string // 子类可以定义自己的属性
}

// 子类重写父类的方法 , 子类的方法名和父类同名,即可重写父类的方法
func (cat Cat) eat() {
	fmt.Println(cat.name, " 正在吃cat....")
}

func main() {
	// 定义一个子类,使用父类方法
	dog := Dog{Animal{name: "旺财", age: 3}}
	dog.eat()  // 调用父类的方法
	dog.wang() // 调用自己扩展的方法

	cat := Cat{Animal{name: "煤球", age: 3}, "黑色"}
	cat.eat() // 如果重写了父类的方法就是调用子类自己的方法

	fmt.Println(cat.color) // 调用子类自己的属性

	// 子类可以操作父类的方法,父类可以操作子类的吗?不可以
	// 父类不能调用子类自己的扩展的方法
	a := Animal{name: "大黄", age: 3}
	a.eat()
	a.sleep()
}

 接口

  • 接口就是把一些共性的方法集合在一起定义

  • 如果有实现类将接口定的方法全部实现了,那么就代表实现了这个接口

  • 接口是方法的定义集合,不需要实现具体的方法内容 

package main

import (
	"fmt"
)

// 接口的定义 interface 来定义,方法太多了,要归类,方法的集合
type USB interface { // 接口,方法的集合
	input()  // 输入方法
	output() // 输出方法
}

// 鼠标
type Mouse struct {
	name  string
	price float64
}

// 键盘
type KeyBoard struct {
	name  string
	price float64
}

// 结构体实现了接口的全部方法就代表实现了这个接口,否则不算实现这个接口
func (mouse Mouse) output() {
	fmt.Println(mouse.name, "鼠标输出")
}
func (mouse Mouse) input() {
	fmt.Println(mouse.name, "鼠标输入")
}

// 结构体实现了接口的全部方法就代表实现了这个接口,否则不算实现这个接口
func (keyboard KeyBoard) output() {
	fmt.Println(keyboard.name, "键盘输入")
}
func (keyboard KeyBoard) input() {
	fmt.Println(keyboard.name, "键盘输入")
}

// 接口的测试
func test(u USB) {
	u.input()
	u.output()
}

func main() {
	// 通过传入接口实现类来进行调用,这样灵活,减少臃肿
	m := Mouse{name: "华为鼠标"}
	// test 参数 USB 类型,传入Mouse也可以呢?因为一个结构体实现了这个接口所有的方法,那这个结构体就是这个接口类型的
	test(m)

	k := KeyBoard{name: "小米键盘"}
	test(k)

	// 定义高级类型  k就升级了  KeyBoard --> USB  向上转型
	var usb USB
	usb = k
	fmt.Println(usb)
	// 接口是无法使用实现类的属性的
	//fmt.Println(usb.name)
}

空接口

  • 不包含任何方法:空接口没有定义任何方法,因此它可以接受任何类型的值。

  • 所有类型包括结构体都默认实现了空接口:Go 中的所有类型都隐式地满足了空接口类型。

  • 空接口可以存储任何的类型:空接口在需要处理未知类型或者多种类型的场景下非常有用。

  • 类型断言:从空接口中取出一个值时,通常需要使用类型断言来确定该值的具体类型,以便进行类型安全的访问。

  • 反射:空接口与反射机制紧密相关,通过反射可以检查存储在空接口中的值的类型,并进行相应的操作。

不包含任何方法:  
package main

import "fmt"

func doSomething(i interface{}) {
	// 空接口i可以接收任何类型的参数,但这里我们不能对它执行任何方法调用
	fmt.Println("用任何类型的值做某事")
}

func main() {
	doSomething(42)
	doSomething("hello")
	doSomething(true)
}
所有类型默认实现了空接口
package main

import "fmt"

func main() {
	// 所有这些类型都隐式地实现了空接口
	var i interface{} = 42      // int
	var j interface{} = "hello" // string
	var k interface{} = true    // bool
	fmt.Println(i, j, k)
}

 存储任何类型: 

package main

import "fmt"

func storeValue(i interface{}) {
	// 这个函数可以使用空接口存储任何类型的值
	fmt.Printf("Stored value: %v\n", i)
}

func main() {
	storeValue(123)
	storeValue("Go is great!")
	storeValue(3.14)
}

类型断言

 

package main

import "fmt"

func process(i interface{}) {
	// 使用类型断言将空接口中的值转换为具体类型
	if s, ok := i.(string); ok {
		fmt.Println("是字符串类型:", s)
	} else {
		fmt.Println("不是字符串类型")
	}

	if s, ok := i.(int); ok {
		fmt.Println("是int类型:", s)
	} else {
		fmt.Println("不是int类型")
	}
}

func main() {
	process("hello")
	process(42) // 这将输出 "Not a string"
}

反射

 

package main

import (
	"fmt"
	"reflect"
)

func inspect(i interface{}) {
	// 使用反射检查空接口中的值
	v := reflect.ValueOf(i)
	fmt.Printf("Type: %s, Value: %v\n", v.Type(), v.Interface())
}

func main() {
	inspect("hello")
	inspect(123)
	inspect(3.14)
}

多态:Go语言中是模拟多态 

一个事物有多种形态 

package main

import "fmt"

// 定义接口
type Animal interface {
	eat()
	sleep()
}

type Dog struct {
	name string
}

func (dog Dog) eat() {
	fmt.Println(dog.name, "Doc--eat")
}
func (dog Dog) sleep() {
	fmt.Println(dog.name, "Doc--sleep")
}

// 接口的测试
func test(animal Animal) {
	animal.eat()
	animal.sleep()
}

// 多态
func main() {

	// Dog 两重身份:1、Dog   2、Animal ,多态
	dog := Dog{name: "旺财"}
	dog.eat()
	dog.sleep()

	// Dog 也可以是 Animal
	// test 参数 Animal 类型,传入Dog也可以呢?因为一个结构体实现了这个接口所有的方法,那这个结构体就是这个接口类型的
	test(dog)

	// 定义一个类型可以为接口类型的变量
	// 实际上所有实现类都可以赋值给这个对象
	var animal Animal // 模糊的 -- 具体化,将具体的实现类赋值给他,才有意义
	animal = dog
	test(animal)
}

 


网站公告

今日签到

点亮在社区的每一天
去签到