GO语言学习之面向对象(3)

GO语言学习之面向对象(3)

6.面向对象编程思想-抽象

6.1抽象的介绍

我们在前面去定义一个结构体时候,实际上就是把一类事物共有的属性

(字段)和行为(方法)提取出来,形成一个物理模型(结构体)。这种研究问题的方法称为抽象

银行账户 属性/字段 账号结构体

​ 1.账户.2.密码.3.余额

​ /行为/方法

​ 1.存款2.取款.3.查询

6.2代码实现
package main

import "fmt"

//定义一个结构体
type Account struct {
	AccountNo string
	Pwd string
	Balance float64
}
//方法
func (account *Account)Cun(money float64,pwd string){
	//看下输入的密码是否正确
	if pwd!=account.Pwd{
		fmt.Println("你输入的密码不正确")
		return
	}
	//查看存款金额是否正确
	if money <=0{
		fmt.Println("你输入的金额不正确:")
		return
	}
	account.Balance+=money
	fmt.Println("存款成功~~~~")
}
//取款
func (account *Account)QuKuan(money float64,pwd string){
	//看下输入的密码是否正确
	if pwd!=account.Pwd{
		fmt.Println("您输入的密码不正确")
		return
	}
	//看看取款金额是否正确
	if money<=0 ||money>account.Balance{
		fmt.Println("账号余额不足")
		return
	}
	account.Balance-=money
	fmt.Println("取款完成")
}
//查询余额
func (account *Account)YuE(pwd string){
	//输入密码
	if pwd!=account.Pwd{
		fmt.Println("你输入的密码不正确。")
		return
	}
	fmt.Printf("您的账号为%v,余额为 %v",account.AccountNo,account.Balance)
}

func main() {
	//测试
	account :=Account{
		AccountNo: "1234567890",
		Pwd:       "XXOO",
		Balance:  200.0,
	}

	account.Cun(1000000,"XXOO")
	account.QuKuan(20000.0,"XXOO")
	account.YuE("XXOO")
}

我们可以改成手动录入数据

7.面向对象编程三大特征-封装

7.1基本介绍

Go具有面向对象编程语言三大特征:封装,继承,多态。

只是实现的方式和其他OOP语言不一样,下面我们开始学习

7.2封装

封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作

7.3封装的理解和好处

1》隐藏实现细节

2》可以对数据进行验证,保证安全合理(Age)

7.4如何体现封装

1》对结构体中的属性进行封装

2》通过方法,包 实现封装

7.5封装的实现步骤

1》将结构体,字段(属性)的首字母小写(不能导出了,其他包不能使用,类似private)

2》给结构体所在包提供一个工厂模式的函数,首字母大写,类似一个构造函数

3》提供一个首字母大写的Set方法(类似其他语言的public),用于对属性判断并赋值

func(var 结构体类型名) SetXxx(参数列表)(返回值列表){

​		//加入数据验证的业务逻辑

​		var .字段=参数

}

4》提供一个首字母大写的Get方法(类似其他语言的public),用于获取属性的值

func(var 结构体类型名) GetXxx(参数列表)(返回值列表){

​		//加入数据验证的业务逻辑

​		var .字段=参数

}

特别说明:

在go开发中,并没有特别强调封装,这点不像Java,所以,不要用Java的语法特性来看待go,go本身对面向对象的特性做了简化的

7.6 快速入门案例

写一个程序 (teacher.go),不能随便查看老师的年龄,工资等隐私,并对输入的年龄进行合理的验证,设计:model包(teacher.go),main包(main.go调用teacher结构体)

model/teacher.go

package model

import "fmt"

type teacher struct {
	Name string
	age int//其他包不能直接访问
	sal float64
}
//写一个工厂模式函数,相当于构造函数
func NewTeacher(name string)*teacher{
	return &teacher{
		Name:name,
	}
}
//为了访问age 和sal 我们编写一对 setxxx方法和 getxxX方法
func (teacher *teacher)SetAge(age int){
	if age>0 && age <150{
		teacher.age=age
	}else {
		fmt.Println("年龄范围不正确。。。")
	}
}
//
func (teacher *teacher)GetAge()int{

	return teacher.age
}
func (teacher *teacher)SetSal(sal float64)  {
	if sal>=3000&&sal<=30000{
		teacher.sal=sal
	}else {
		fmt.Println("薪水不符合标准")
	}

}
func (teacher *teacher)GetSal()float64{
	return teacher.sal
}

main/main.go

package main

import (
	"byteDemo07/model"
	"fmt"
)

func main() {
	//teacher 结构体是首字母小写,我们通过工厂模式来解决
	tea:=model.NewTeacher("张老师")
	tea.SetAge(28)
	tea.SetSal(29999)
	fmt.Println(tea)
	fmt.Println(tea.Name,"age=",tea.GetAge(),"sal=",tea.GetSal())
}
7.7小练习

要求:

1》创建程序,在model包中定义Account结构体;在main函数中体会GO的封装性

2》Account的结构体要求具有字段:

账号:(长度在6–10之间)

余额:(必须>10)

密码必须六位

3》通过SetXXX的方法给Account的字段赋值

4》在main函数中进行测试

model:包

package model

import "fmt"

//定义一个结构体account
type account struct {
	accountNo string
	pwd string
	balance float64
}
//工厂模式函数
func NewAccount(accountNo string,pwd string,balance float64)*account{
	if len(accountNo)<6||len(accountNo)>10{
		fmt.Println("账号不对")
		return nil
	}
	if len(pwd)!=6{
		fmt.Println("密码长度不对")
		return nil
	}
	if balance<10{
		fmt.Println("余额数目不对...")
		return nil
	}
	return &account{
		accountNo: accountNo,
		pwd:      pwd,
		balance:   balance,
	}
}
//方法
//1.存款
func (account *account)Cun(money float64,pwd string){

	//密码校验
	if pwd!=account.pwd{
		fmt.Println("你输入的密码不正确")
		return
	}
	//存款金额校验
	if money<=0{
		fmt.Println("您输入的金额有误。。。")
		return
	}
	account.balance+=money
	fmt.Println("存款成功。。。")
}
//取款
func (account *account)	QuKuan(money float64,pwd string){
	//看下输入的密码是否正确
	if pwd!=account.pwd{
		fmt.Println("你输入的密码不正确。。。")
		return
	}
	//看看取款金额是否正确
	if money<=0||money>account.balance{
		fmt.Println("您输入金额不正确")
		return
	}
	account.balance-=money
	fmt.Println("取款成功")
}
//查余额
func (account *account)YuE(pwd string){
	//看输入密码是否正确
	if pwd!=account.pwd{
		fmt.Println("您输入密码不正确:")
		return
	}

	fmt.Printf("你的账户为:%v 余额为:%v \n",account.accountNo,account.balance)
}



main包:

package main

import (
	"byteDemo07/model"
	"fmt"
)

func main() {
	//teacher 结构体是首字母小写,我们通过工厂模式来解决
	tea:=model.NewTeacher("张老师")
	tea.SetAge(28)
	tea.SetSal(29999)
	fmt.Println(tea)
	fmt.Println(tea.Name,"age=",tea.GetAge(),"sal=",tea.GetSal())
}

8.面向对象编程三大特征-继承

8.1看一个问题,引出继承的必要性
package main

import "fmt"

//编写一个学生考试系统

//小学生
type Small struct {
	Name string
	Age int
	Score int
}
//显示他的成绩
func (s *Small)ShowInfo(){
	fmt.Printf("姓名:%v,年龄:%v,成绩:%v\n",s.Name,s.Age,s.Score)
}
//
func (s *Small)SetScore(score int)  {

	s.Score=score
}
func (s *Small)testing()  {

	fmt.Println("小学生正在考试。。。")
}

//大学生,研究生
type Big struct {
	Name string
	Age int
	Score int
}
//显示他的成绩
func (s *Big)ShowInfo(){
	fmt.Printf("姓名:%v,年龄:%v,成绩:%v\n",s.Name,s.Age,s.Score)
}
//
func (s *Big)SetScore(score int)  {

	s.Score=score
}
func (s *Big)testing()  {

	fmt.Println("小学生正在考试。。。")
}
//初中生
//高中生

func main() {

	var s  =&Small{
		Name:  "tom",
		Age:   10,
		Score: 100,
	}

	s.SetScore(90)
	s.ShowInfo()
	s.testing()

	var b  =&Big{
		Name:  "tom",
		Age:   10,
		Score: 100,
	}

	b.SetScore(90)
	b.ShowInfo()
	b.testing()
}

》对上面代码小结

1》Small和 Big 两个结构体的字段和方法几乎,但是我们却写了相同的代码,代码复用性不强

2》代码冗余,而且不利于维护,同时也不利于功能的扩展

3》解决方法–通过继承方式来解决

8.2继承基本介绍和示意图

继承可以解决代码复用,让我们的编程更加靠近人类思维

当多个结构体存在相同属性(字段)和方法时,可以从这些结构体中抽象出结构体(比如Student),在该结构体中定义这些相同的属性和方法

其他的结构体不需要重新定义这些属性(字段)和方法,只需要嵌套一个Studnet匿名结构体即可

student <共有字段,共有方法)

small(自有字段,自有方法)Big(自有字段,自有方法)

在go中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现继承性

8.3嵌套匿名结构体的基本语法

type Students struct{

Name string

Age int

Score int

}

type Smalls struct{

Students//这里嵌套的就是匿名结构体Studnets

hight int

}

8.4快速入门案例

我们对8.1中代码,使用匿名结构体的方式来实现继承特性,请大家注意体会这样编程的好处

package main

import "fmt"

//编写一个学生考试系统

type Student struct {
	Name string
	Age int
	Score int
}

//显示他的成绩
func (s *Student)ShowInfo(){
	fmt.Printf("姓名:%v,年龄:%v,成绩:%v\n",s.Name,s.Age,s.Score)
}
//
func (s *Student)SetScore(score int)  {

	s.Score=score
}


//小学生
type Small struct {
	Student
}
//小学生特有方法
func (s *Small)testing()  {

	fmt.Println("学生正在考试。。。")
}
//大学生
type Big struct {
	Student
}

func (b *Big)testing()  {

	fmt.Println("大学时在考试")
}

//
func main() {
	//当我们对结构体嵌入匿名结构体使用方法会发生变化
	s:=&Small{}
	s.Student.Name="tom"
	s.Student.Age=100
	s.Student.SetScore(88)
	s.ShowInfo()

}
8.5继承给编程带来的便利

1》提高了代码的可维护性

2》代码的扩展性和维护性提高了

8.6继承的深入讨论

1》结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段,方法,都可以使用

func main() {
	//当我们对结构体嵌入匿名结构体使用方法会发生变化
	s:=&Small{}
	s.Student.Name="tom"
	s.Student.Age=100
	s.Student.SetScore(88)
	s.ShowInfo()

}

2》匿名结构体字段访问可以简化

func main() {
	//当我们对结构体嵌入匿名结构体使用方法会发生变化
	s:=&Small{}
	s.Name="tom"
	s.Age=100
	s.SetScore(88)
	s.ShowInfo()

}

对上面的代码总结:

(1)当我们直接通过b访问字段或方法时,其执行流程如下:s.Name

(2)编辑器会先看s对应的类型有没有Name ,如果有,则直接调用s类型的Name字段

(3)如果没有就去看B中·嵌入的匿名结构体A有没有声明Name字段,如果有就调用,如果没有就继续查找。。。如果都找不到就报错

3》当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分

	s.SetScore(88)
	s.Studnet.SetScore(88)

4》结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译错误

type A struct{
	Name string
	age int
}
type B struct{
	Name string
	Score int
}
type C struct{
	A
	B
}

func main(){
	var c C
	c.A.Name="lilu"//必须指定引自谁
	fmt.Println(c)
}

5》如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或者方法时,必须带上结构体的名字

type D struct{
	a A//有名结构体
}
//如果D中国是一个有名结构体,则访问有名结构体的字段时,就必须带上有名结构体的名字
var d D
d.a.Name="jack"

6》嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值

type Goods struct {
	Name string
	Price float64
}
type Brand struct {
	Name string
	Size float64
}
type TV struct {
	Goods
	Brand
}
type TV2 struct {
	*Goods
	*Brand
}
//嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
tv:=TV{Goods{"空调",5999.0},Brand{"GELEE",3.0}}

	tv2:=TV{
		Goods{
			Name:  "冰箱",
			Price: 1999.0,
		},
		Brand{
			Name: "海尔",
			Size: 5.0,
		},
	}

	fmt.Println("tv",tv)
	fmt.Println("tv2",tv2)

	tv3:=TV2{&Goods{"空调2",8888.0},&Brand{"美的",1.8}}

	tv4:=TV2{
		&Goods{
			Name:"饮水机",
			Price:199.9,
		},
		&Brand{
			Name:"美的",
			Size:2.0,
		},
	}

	fmt.Println("tv3",*tv3.Goods,*tv3.Brand)
	fmt.Println("tv4",*tv4.Goods,*tv4.Brand)

8.7练习

结构体的匿名字段是基本数据类型,如何访问,下面代码输出什么

package main

import "fmt"

type Goods struct {
	Name string
	Price float64
}
type A struct {
	Goods
	int
	Num int
}

func main() {
	var a A
	a.Name="热水器"
	a.Price=18.8
	a.int=200
	a.Num=500

	fmt.Println(a)

}

说明:

1》如果一个结构体有Int类型的匿名字段,就不能第二个

2》如果需要有多个int的字段,则必须给int字段指定名字

8.8面向对象编程-多重继承

》多重继承说明

如一个struct 嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承

》案例演示

通过一个案例来说明多重继承的使用

type Goods struct {
	Name string
	Price float64
}
type Brand struct {
	Name string
	Size float64
}
type TV struct {
	Goods
	Brand
}

注意:

如果嵌入的匿名结构体有相同的字段或者方法名,则在访问时,需要通过匿名结构体类型来区分:

//演示访问: Goods 的name
fmt.Println(tv.Goods.Name)
fmt.Println(tv.Price)

为了保证代码的简洁性,建议大家尽量不要使用多重继承

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值