Go 1.19.4 面向对象-Day 12

1. 面向对象介绍

1.1 如何理解面向对象?

面向对象中有2个概念:类和实例。
(1)类(type):同一类事物的共同特征的抽象概念。
(2)实例(instance):某一种类型的某一个具体的实体(看得见摸得着)。

比如说:人类,就属于类这个抽象概念。我这个人,属于人类吧,但同时也是人类的具体的一个实例。

1.2 面向对象三要素

(1)封装
  将属性(数据)和方法(操作)封装,提供访问控制(标识符大小写),隐藏实现细节,暴露该暴露的

类似下面的代码:

type User struct { // 这就是属性
    id         int
    name, addr string
    score      float32
}

func getName(u User) string {  // 这就是方法
    return u.name
}


(2)继承
  子类可以从父类直接获得属性和方法,减少重复定义(类似于结构体嵌套)。

  子类如果不满意父类的属性和方法,可以自己定义新的属性和方法,也可以覆盖同名的属性和方法


(3)多态
  前提是继承和覆盖,使得子类中虽然使用同一个方法,但是不同子类表现不同,就是不同的态

实现了以上特征的语言,才能成为面向对象编程范式语言。

严格意义来说,Go语言就是不想实现面向对象编程范式。但是面向对象又有一些不错的特性,Go语言 通过组合的方式实现了类似的功能。

只能说,Go语言实现了一种非常有自我特征的面向对象。

2. 封装

2.1 什么是封装

通过结构体,可以把数据字段封装在内,还可以为结构体提供方法。

2.2 访问控制

(1)包外可见

        属性、方法标识符首字母大写。

(2)包内可见

        属性、方法标识符首字母小写。

这些一定程度上实现了public、private的访问控制。

3. 构造函数

Go没有提供类似C++、Java一样的构造函数、析构函数。

 在Go中,用构造结构体实例的函数,这个函数没有特别的要求,只返回结构体实例或其指针即可(建议返回指针,不然返回值会拷贝),就相当于实现了构造函数

习惯上, 构造函数命名是New或new开头。如果有多个构造函数,可以使用不同命名函数,因为Go也没有函数重载。

type Animal struct {
	name string
	age int
}

// New开头,返回结构体指针。变相实现了构造函数
func NewDefaultAnimal() *Animal {
	return &Animal{"Tom", 18}
}

func NewAnimal(name string, age int) *Animal {
	return &Animal{name, age}
}

4. 继承

Go语言没有提供继承的语法,实际上需要通过匿名结构体嵌入(组合)来实现类似效果。

package main

import "fmt"

// 动物类(所有动物的父类)
type Animal struct {
	name string
	age  int
}

func (*Animal) run() {
	fmt.Println("这里是Animal动物类的run方法")
}

// 猫类
type Cat struct {
	Animal // 嵌入父类,所以猫类(Cat)是动物类(Animal)的子类
	color  string
}

func main() {
	cat := new(Cat)
	// 由于Cat类本身没有实现run()方法,所以只能去父类Animal中调用,因为父类实现了run()方法
	cat.run()
	
	cat.Animal.run()
}
============调试结果============
这里是Animal动物类的run方法
这里是Animal动物类的run方法

5. 覆盖(override)

也称重写。注意不是重载overload。

package main

import "fmt"

// 动物类(所有动物的父类)
type Animal struct {
	name string
	age  int
}

func (*Animal) run() {
	fmt.Println("这里是Animal动物类的run方法")
}

// 猫类
type Cat struct {
	Animal
	color string
}

// 我觉得父类的run()方法,不满足我的需求,于是我重新定义了一个run()方法
// 因为Cat类中嵌入了父类Animal,所以我们重新定义run,就相当于覆盖了父类的run
func (a *Cat) run() {
	fmt.Println("这里是Cat猫类的run方法")
}

func main() {
	cat := new(Cat)

	cat.run() // 这里调用run方法的时候,就会直接调用Cat类的run方法

	cat.Animal.run() // 这里因为指定调用父类的run方法,所以结果不变
}
============调试结果============
这里是Cat猫类的run方法
这里是Animal动物类的run方法

为Cat增加一个run方法,这就是覆盖。特别注意 cat.run() 和 cat.Animal.run() 的区别。

上例增加run方法是完全覆盖,就是不依赖父结构体方法,重写功能。

如果是依赖父结构体方法,那就要在子结构体方法中调用它。

func (c *Cat) run() {
 c.run()        // 可以吗?不可以,cat.run() 这是无限递归调用,小心!
 c.Animal.run() // 可以吗? 可以,这就调用了父结构体中的方法
 fmt.Println("Cat run+++")
}

6. 多态

实现多态的前提:先实现继承和覆盖,使得子类中虽然使用同一个方法,但是不同子类表现不同,就是不同的态。

Go语言不能像Java语言一样使用多态。可以通过引入interface接口来解决。

package main

import "fmt"

// 动物类(所有动物的父类)
type Animal struct {
	name string
	age  int
}

func (a *Animal) run() {
	fmt.Printf("%s 这里是Animal动物类(父类)的run方法", a.name)
}

// 猫类
type Cat struct {
	Animal
	color string
}

func (a *Cat) run() {
	fmt.Println("这里是Cat猫类的run方法")
}

// 狗类
type Dog struct {
	Animal
	color string
}

func main() {
	d := new(Dog)

	d.name = "旺财"
	
	// 这里由于Dog本身没有实现run()方法,所以只能去父类Animal中调用run()方法
	d.run() 
}

============调试结果============
旺财 这里是Animal动物类(父类)的run方法

首先我们有3个类
Animal(父类)
  Cat(子类)
  Dog(子类)
  
假设小花,是猫类的一个具体的实例,属于Cat猫类,同时也属于Animal动物类。
假设旺财,是狗类的一个具体的实例。属于Dog狗类,同时也属于Animal动物类。

那上述代码跟多态有什么关系呢?继续看下面的代码:

package main

import "fmt"

// 动物类(所有动物的父类)
type Animal struct {
	name string
	age  int
}

func (a *Animal) run() {
	fmt.Printf("%s 这里是Animal动物类(父类)的run方法", a.name)
}

// 猫类
type Cat struct {
	Animal
	color string
}

func (a *Cat) run() {
	fmt.Printf("%s 这里是Cat猫类的run方法\n", a.name)
}

// 狗类
type Dog struct {
	Animal
	color string
}

func (a *Dog) run() {
	fmt.Printf("%s 这里是Dog狗类的run方法\n", a.name)
}


func main() {
	var c = new(Cat)
	c.name = "三花"
	c.run()

	d := new(Dog)
	d.name = "旺财"
	d.run()
}
============调试结果============
三花 这里是Cat猫类的run方法
旺财 这里是Dog狗类的run方法

注意看上述代码的结果,c和d都调用的run方法,并输出了对应的结果。

但有没有办法,调用一次run,实现输出不同的结果呢?看下面:

6.1 基于接口实现多态

package main

import "fmt"

// 定义用来实现多态的接口
type Runner interface {
	run()
}

type Animal struct {
	name string
	age  int
}

func (a *Animal) run() {
	fmt.Printf("%s 这里是Animal动物类(父类)的run方法", a.name)
}

type Cat struct {
	Animal
	color string
}

func (a *Cat) run() {
	fmt.Printf("%s 这里是Cat猫类的run方法\n", a.name)
}

type Dog struct {
	Animal
	color string
}

func (a *Dog) run() {
	fmt.Printf("%s 这里是Dog狗类的run方法\n", a.name)
}

// 实现多态
func foo(a Runner) {
	a.run()
}

func main() {
	var c = new(Cat)
	c.name = "三花"

	d := new(Dog)
	d.name = "旺财"

	// 调用foo函数实现多态
	foo(c)
	foo(d)
}
============调试结果============
三花 这里是Cat猫类的run方法
旺财 这里是Dog狗类的run方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值