go语言基础--面向对象杂谈

面向过程

所谓的面向过程就是:强调的是步骤、过程、每一步都是自己亲自去实现的。

面向对象

所谓的面向对象其实就是找一个专门做这个事的人来做,不用关心具体怎么实现的。

所以说,面向过程强调的是过程,步骤。而面向对象强调的是对象,也就是干事的人。

在程序中,可以通过属性和方法(函数)来描述类。属性就是特征,方法(函数)就是行为。

面向对象编程好处

封装

继承

多态

继承

继承是一种类间关系,描述一个类从另一个类获取成员信息的类间关系。

继承必定发生在两个类之间,参与继承关系的双方称为父类和子类。

父类提供成员信息,子类获取成员信息。

通过匿名字段来实现继承

go 复制代码
package main

import "fmt"

type Student struct {
	//属性---成员
	//方法---函数
	Person //匿名字段,只有类型,没有成员的名字

	score float64
}

type Teacher struct {
	Person //匿名字段,只有类型,没有成员的名字
	salary float64
}

type Person struct {
	id   int
	name string
	age  int
}

func main() {
	var stu Student = Student{Person{1, "张三", 18}, 98}
	fmt.Println(stu) //{{1 张三 18} 98}

	//部分初始化
	var stu1 Student = Student{score: 90}
	fmt.Println(stu1) //{{0  0} 90}

	var stu2 Student = Student{Person: Person{id: 101}} //{{101  0} 0}
	fmt.Println(stu2)
}

成员操作

go 复制代码
package main

import "fmt"

type Student struct {
	//属性---成员
	//方法---函数
	Person //匿名字段,只有类型,没有成员的名字

	score float64
}

type Person struct {
	id   int
	name string
	age  int
}

func main() {
	var stu Student = Student{Person{101, "张三", 18}, 98}
	var stu1 Student = Student{Person{102, "李四", 18}, 80}

	//获取成员的值
	fmt.Println(stu.score)      //98
	fmt.Println(stu1.score)     //80
	fmt.Println(stu1.Person.id) //102 //比较麻烦
	fmt.Println(stu1.id)        //102

	//修改成员的值
	stu.score = 100
	fmt.Println(stu.score) //100

}

指针类型匿名字段

go 复制代码
package main

import "fmt"

type Student struct {
	//属性---成员
	//方法---函数
	*Person //指针类型匿名字段

	score float64
}

type Person struct {
	id   int
	name string
	age  int
}

func main() {
	var stu Student = Student{&Person{101, "张三", 18}, 98}
	fmt.Println(stu)      //{0xc000054460 98} 父类输出的是结构体内存地址 只能使用成员操作了
	fmt.Println(stu.name) //张三
}

多重继承---尽量不要写多重继承

go 复制代码
package main

import "fmt"

// Student 学生继承person
type Student struct {
	//属性---成员
	//方法---函数
	Person //指针类型匿名字段

	score float64
}

// Person 父类 person继承object
type Person struct {
	Object1
	name string
	age  int
}

type Object1 struct {
	id int
}

func main() {

	var stu Student
	stu.age = 18
	fmt.Println(stu.Person.age) //18
	stu.id = 101
	fmt.Println(stu.Person.Object1.id)
}

为结构体添加方法---封装

go 复制代码
func  (对象 结构体类型) 方法名 (参数列表)(返回值列表) {
	
		代码体

}

方法调用

对象名.方法

//不支持重载,只要接收者类型不一样,这个方法就算同名,也是不同方法,不会出现重复定义函数的错误

go 复制代码
package main

import "fmt"

// Student
type Student struct {
	id   int
	name string
	age  int
}

// PrintShow 方法 s为接收者
func (s Student) PrintShow() {
	fmt.Println(s) //{101 ziye 18}
}

func (s Student) EditInfo() {
	s.age = 20
}

func (s *Student) EditInfo1() {
	s.age = 20
}
func main() {
	stu := Student{101, "ziye", 18}
	//对象名.方法名 把stu中的值传给了s
	stu.PrintShow() //完成对方法的调用
	stu.EditInfo()  //不是引用传递,是值传递
	stu.PrintShow()
	//内部会进行转换
	stu.EditInfo1() //引用传递
	stu.PrintShow() //{101 ziye 20}
}

注意事项

只要接收者类型不一样,这个方法就算同名,也是不同方法

接收者为指针类型

go 复制代码
package main

import "fmt"

// Student
type Student struct {
	id   int
	name string
	age  int
}

type Teacher struct {
	id   int
	name string
}

func (s *Student) show() {
	fmt.Println(s)
}

func (t *Teacher) show() { //把teacher内存地址给到这里
	fmt.Println(t)
}
func main() {
	//如果接收者类型不同,即使方法的名字是相同的也是不同的方法
	stu := Student{101, "ziye", 18}
	stu.show() //等价于(&stu).show()
	teacher := Teacher{102, "ziyeye"}
	teacher.show()
}

面向对象方法练习

定义一个学生类,有六个属性,分别为姓名、性别、年龄、语文、数学、英语成绩

定义两个方法:

复制代码
第一方法:打招呼的方法:介绍自己叫XX,今年几岁了。是男同学还是女同学。
第二个方法:计算总分与平均分的方法
go 复制代码
package main

import "fmt"

// Student
type StudentInfo struct {
	name    string  //姓名
	sex     string  //性别
	age     int     //年龄
	chinese float64 //语文
	math    float64 //数学
	english float64 //英语
}

// SayHello 打招呼
func (studentInfo *StudentInfo) SayHello(username string, userAge int, userSex string) {
	//初始化
	studentInfo.name = username
	studentInfo.age = userAge
	studentInfo.sex = userSex
	//初始化后的值进行判断
	if studentInfo.sex != "男" && studentInfo.sex != "女" {
		studentInfo.sex = "男"
	}

	if studentInfo.age < 1 || studentInfo.age > 100 {
		studentInfo.age = 18
	}
	//打印输出结果
	fmt.Printf("我叫%s,年龄是%d,性别是%s\n", studentInfo.name, studentInfo.age, studentInfo.sex)
}

// GetScore 计算平均分
func (studentInfo *StudentInfo) GetScore(chinese float64, math float64, english float64) {
	//初始化
	studentInfo.chinese = chinese
	studentInfo.math = math
	studentInfo.english = english
	//进行计算
	sum := studentInfo.chinese + studentInfo.math + studentInfo.english
	//打印输出结果
	fmt.Printf("我叫%s,总分%f,平均分%.2f\n", studentInfo.name, sum, sum/3)
}
func main() {
	var stu StudentInfo
	stu.SayHello("ziye", 18, "女")
	stu.GetScore(98, 97, 95)
}

方法继承

go 复制代码
package main

import "fmt"

// Student
type Student struct {
	Person
	score float64
}

type Person struct {
	id   int
	name string //姓名
	age  int    //年龄
}

func (p *Person) PrintInfo() {
	fmt.Println(*p) //{101 张三 18}
}

func main() {
	stu := Student{Person{101, "张三", 18}, 90}
	//子类可以调用父类的方法
	stu.PrintInfo()
}

方法继承的练习

根据以下信息,实现对应的继承关系

记者:我叫张三 ,我的爱好是偷拍,我的年龄是34,我是一个男狗仔。

程序员:我叫孙全,我的年龄是23,我是男生,我的工作年限是 3年。

go 复制代码
  package main

import "fmt"

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

// SetValue 给父类添加方法
func (p *Person) SetValue(userName string, userAge int, userSex string) {
	p.name = userName
	p.age = userAge
	p.sex = userSex
}

// Rep 定义相应的子类 记者类
type Rep struct {
	Person
	Hobby string //爱好
}

// Pro 程序员类
type Pro struct {
	Person
	WorkYear int
}

// RepSayHello 给子类添加相应的信息
func (r *Rep) RepSayHello(Hobby string) {
	r.Hobby = Hobby
	fmt.Printf("我叫%s ,我的爱好是%s,我的年龄是%d,我是一个%s狗仔\n", r.name, r.Hobby, r.age, r.sex)
}

// ProSayHello 给子类添加相应的信息
func (p *Pro) ProSayHello(workYear int) {
	p.WorkYear = workYear
	fmt.Printf("我叫%s,我的年龄是%d,我是%s,我的工作年限是 %d年\n", p.name, p.age, p.sex, p.WorkYear)
}
func main() {
	var rep Rep
	rep.SetValue("ziye", 34, "男")
	rep.RepSayHello("偷拍")

	var pro Pro
	pro.SetValue("李四", 26, "男")
	pro.ProSayHello(3)

}

方法重写

就是子类(结构体)中的方法,将父类中的相同名称的方法的功能重新给改写了

注意:在调用时,默认调用的是子类中的方法

go 复制代码
package main

import "fmt"

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

func (p *Person) PrintInfo() {
	fmt.Println("这是父类中的方法")
}

type Student struct {
	Person
	score float64
}

func (s *Student) PrintInfo() {
	fmt.Println("这是子类中的方法")
}

func main() {
	var stu Student
	stu.PrintInfo() //这是子类中的方法 如果父类中的方法名称与子类中的方法一致,那么通过子类的对象调用的是子类中的方法,方法重写
	stu.Person.PrintInfo() //这是父类中的方法 调用父类中的方法
}

方法值与方法表达式

go 复制代码
package main

import "fmt"

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

func (p *Person) PrintInfo() {
	fmt.Println(*p) //{ziye 18}
}

func main() {
	per := Person{"ziye", 18}
	per.PrintInfo()

	//方法值
	f := per.PrintInfo
	fmt.Printf("%T\n", f) //func() 方法类型
	f()                   //{ziye 18}

	//方法表达式
	f1 := (*Person).PrintInfo //并没有指定一个对象 类名要和方法接收者类型保存一致
	f1(&per)                  //{ziye 18} 方法表达式
}

接口简介

接口就是一种规范与标准,只是规定了要做哪些事情。具体怎么做,接口是不管的。

接口把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实

现了这个接口。

接口定义

type 接口名字 interface {

复制代码
方法声明

}

接口的声明

可以为结构体添加接口中的方法,完成接口中方法实现

go 复制代码
package main

import "fmt"

// Personer 接口的声明
type Personer interface {
	SayHello() //方法声明
}

type Student struct {
}

// SayHello 使用student完成SayHello
func (s *Student) SayHello() {
	fmt.Println("老师好")
}

type Teacher struct {
}

func (t *Teacher) SayHello() {
	fmt.Println("学生好")
}

func main() {
	//对象名.方法名
	var stu Student
	stu.SayHello() //老师好
	var teacher Teacher
	teacher.SayHello() //学生好
	//接口变量来调用,必须都实现接口中所声明的方法
	var person Personer
	person = &stu
	person.SayHello() //调用的是Student、实现的SayHello方法 老师好

	person = &teacher
	person.SayHello() //学生好

}

多态的定义与实现

什么是多态

所谓多态:指的是多种表现形式。

多态就是同一个接口,使用不同的实例而执行不同操作

go 复制代码
package main

import "fmt"

type Personer interface {
	SayHello()
}

type Student struct {
}

func (s *Student) SayHello() {
	fmt.Println("老师好")
}

type Teacher struct {
}

func (t *Teacher) SayHello() {
	fmt.Println("学生好")
}

//实现多态
func WhoSayHi(personer Personer) {
	personer.SayHello()
}

func main() {

	var stu Student
	var teacher Teacher
	WhoSayHi(&stu)
	WhoSayHi(&teacher)
}

案例

用多态来模拟实现 将移动硬盘或者U盘插到电脑上进行读写数据

go 复制代码
package main

import "fmt"

type Stroager interface {
	Read()
	Writer()
}

// MDisk 移动硬盘
type MDisk struct {
}

func (m *MDisk) Read() {
	fmt.Println("移动硬盘读取数据")
}

func (m *MDisk) Writer() {
	fmt.Println("移动硬盘写入数据")
}

// UDisk U盘
type UDisk struct {
}

func (u *UDisk) Read() {
	fmt.Println("U盘读取数据")
}

func (u *UDisk) Writer() {
	fmt.Println("U盘写入数据")
}

// Computer 定义一个函数
func Computer(s Stroager) {
	s.Writer()
	s.Read()
}
func main() {
	var uds UDisk
	var mds MDisk
	Computer(&uds)
	Computer(&mds)
}

案例

使用面向对象方式,实现一个计算器程序

案例实现一

go 复制代码
package main

import "fmt"

type Object1 struct {
}

func (o *Object1) GetResult(num1, num2 int, op string) int {
	//添加参数
	var result int
	switch op {
	case "+":
		result = num1 + num2
	case "-":
		result = num1 - num2
	}
	return result
}

func main() {
	var obj Object1
	result := obj.GetResult(8, 6, "+")
	fmt.Println(result)
}

案例实现二

go 复制代码
package main

import "fmt"

// 加法类
type Add struct {
	Object1
}

func (add *Add) GetResult() int { //方法的实现要和接口中方法的声明保持一致
	return add.numA + add.numB
}

type Sub struct {
	Object1
}

func (sub *Sub) GetResult() int {
	return sub.numA - sub.numB
}

type Object1 struct {
	numA int
	numB int
}

type Resulter interface {
	GetResult() int //返回类型int
}

func main() {
	add := Add{Object1{10, 8}}
	result := add.GetResult()
	fmt.Println(result)

	sub := Sub{Object1{10, 8}}
	result = sub.GetResult()
	fmt.Println(result)
}

案例实现三

go 复制代码
package main

import "fmt"

// 加法类
type Add struct {
	Object1
}

func (add *Add) GetResult() int { //方法的实现要和接口中方法的声明保持一致
	return add.numA + add.numB
}

type Sub struct {
	Object1
}

func (sub *Sub) GetResult() int {
	return sub.numA - sub.numB
}

type Object1 struct {
	numA int
	numB int
}

type Resulter interface {
	GetResult() int //返回类型int
}

// OperatorFactory 对象创建问题
// 1:定义一个新的类
type OperatorFactory struct {
}

// CreaeteOperator 创建一个方法,在该方法中完成对象的创建----封装
func (o *OperatorFactory) CreaeteOperator(op string, numA, numB int) int {
	switch op {
	case "+":
		add := Add{Object1{numA, numB}}
		return OperatorWho(&add)
	case "-":
		sub := Sub{Object1{numA, numB}}
		return OperatorWho(&sub)
	default:
		return 0
	}
}
// 多态
func OperatorWho(h Resulter) int {
	result := h.GetResult()
	return result
}
func main() {
	var operator OperatorFactory
	creaeteOperator := operator.CreaeteOperator("+", 20, 10)
	fmt.Println(creaeteOperator)
}

接口的继承与转换

go 复制代码
package main

import "fmt"

type Humaner interface {
	SayHello()
}

// Personer 接口继承
type Personer interface {
	Humaner
	Say()
}
type Student struct {
}

func (s *Student) SayHello() {
	fmt.Println("大家好")
}

func (s *Student) Say() {
	fmt.Println("你好")
}

func main() {
	var Stu Student
	var per Personer
	per = &Stu
	per.Say()
	per.SayHello() //可以调用所继承的接口中的方法

	//接口转换
	var h Humaner
	h = per
	h.SayHello()
	//超集可以转换为子集,反过来不可以
	//per = h
}

空接口定义与使用

go 复制代码
var i interface{} //空接口
i = 123
fmt.Println(i)

空接口可以赋任意的类型,切片s可以赋值各种类型的

go 复制代码
var s []interface{} //空接口切片s
	s = append(s, 123, "abc", 23.12)
	for i := 0; i < len(s); i++ {
		fmt.Println(s[i])
	}

空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值

类型断言

通过类型断言,可以判断空接口中存储的数据类型。

语法:value, ok := m.(T)

m:表空接口类型变量

T:是断言的类型

value: 变量m中的值。

ok: 布尔类型变量,如果断言成功为true,否则为false

go 复制代码
package main

import "fmt"

func main() {
	var i interface{}
	i = 123
	value, ok := i.(int)

	if ok {
		fmt.Println(value)
	} else {
		fmt.Println("类型推断错误")
	}
}

案例--空接口与类型断言综合应用

计算器,完成数据校验

go 复制代码
package main

import "fmt"

// 加法类
type Add struct {
	Object1
}

func (add *Add) GetResult() int { //方法的实现要和接口中方法的声明保持一致
	return add.numA + add.numB
}

func (add *Add) SetData(data ...interface{}) bool {
	//对数据的个数进行校验
	var b bool = true
	if len(data) > 2 || len(data) <= 1 {
		fmt.Println("参数个数错误")
		b = false
	}
	value, ok := data[0].(int)
	if !ok {
		fmt.Println("第一个数类型错误")
		b = false
	}

	value1, ok1 := data[1].(int)
	if !ok1 {
		fmt.Println("第二个数类型错误")
		b = false
	}
	add.numA = value
	add.numB = value1
	//对传递过来的类型进行校验
	return b
}

type Sub struct {
	Object1
}

func (sub *Sub) GetResult() int {
	return sub.numA - sub.numB
}
func (sub *Sub) SetData(data ...interface{}) bool {
	//对数据的个数进行校验
	var b bool = true
	if len(data) > 2 || len(data) <= 1 {
		fmt.Println("参数个数错误")
		b = false
	}
	value, ok := data[0].(int)
	if !ok {
		fmt.Println("第一个数类型错误")
		b = false
	}

	value1, ok1 := data[1].(int)
	if !ok1 {
		fmt.Println("第二个数类型错误")
		b = false
	}
	sub.numA = value
	sub.numB = value1
	//对传递过来的类型进行校验
	return b
}

type Object1 struct {
	numA int
	numB int
}

type Resulter interface {
	GetResult() int                   //返回类型int
	SetData(data ...interface{}) bool //完成参数运算的数据的类型校验
}

// OperatorFactory 对象创建问题
// 1:定义一个新的类
type OperatorFactory struct {
}

// CreaeteOperator 创建一个方法,在该方法中完成对象的创建----封装
func (o *OperatorFactory) CreaeteOperator(op string) Resulter {
	switch op {
	case "+":
		add := new(Add) //返回*add
		return add
	case "-":
		sub := new(Sub) //创建Sub 开辟相应的存储空间
		return sub
	default:
		return nil
	}
}

// 多态
func OperatorWho(h Resulter) int {
	result := h.GetResult()
	return result
}
func main() {
	var operator OperatorFactory
	creaeteOperator := operator.CreaeteOperator("-") //拿到对象
	setData := creaeteOperator.SetData(30, 10)
	if setData {
		who := OperatorWho(creaeteOperator)
		fmt.Println(who)
	}

}
相关推荐
Dovis(誓平步青云)31 分钟前
《QT学习第四篇:常见事件与UDP、TCP、文件系统、(锁、信号量、条件变量》
c语言·开发语言·汇编·qt
llz_1122 小时前
web-第二次课后作业
前端·后端·web
红尘散仙7 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记9 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
isyangli_blog9 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008119 小时前
FastAPI APIRouter
开发语言·python
Benszen9 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆9 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木9 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
喵个咪9 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm