golang 面向对象的三大特性
golang有面向对象是三大特征:继承,封装和多态。
1.抽象
把一类事务的共有属性(字段)和行为(方法)提取出来,形成一个物理模型(结构体),这种研究问题的方法称为抽象。
示例:银行存取款
package main import "fmt" type Account struct { AccountNo string Pwd string Balance float64 } //1.存款 func (account *Account) Deposite(money float64, pwd string) { //看输入的密码是否正确 if pwd != account.Pwd { fmt.Println("你输入的密码不正确") return } //看看存款金额是否正确 if money <= 0 { fmt.Println("你输入的存款金额不正确") return } account.Balance += money fmt.Println("存款成功") } //1.取款 func (account *Account) WithDraw(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("取款成功") } //1.查询余额 func (account *Account) Query(pwd string) { //看输入的密码是否正确 if pwd != account.Pwd { fmt.Println("你输入的密码不正确") return } fmt.Printf("你的账户为=%v,余额为=%v\n", account.AccountNo, account.Balance) } func main() { account := Account{ "gs1111", "6666", 100.0, } account.Query("6666") account.Deposite(20, "6666") account.Query("6666") account.WithDraw(10, "6666") account.Query("6666") }
用户端输入示例
package main import ( "fmt" ) type Account struct { AccountNo string Pwd string Balance float64 } //1.存款 func (account *Account) Deposite(money float64, pwd string) { //看输入的密码是否正确 if pwd != account.Pwd { fmt.Println("你输入的密码不正确") return } //看看存款金额是否正确 if money <= 0 { fmt.Println("你输入的存款金额不正确") return } account.Balance += money fmt.Println("存款成功") } //1.取款 func (account *Account) WithDraw(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("取款成功") } //1.查询余额 func (account *Account) Query(pwd string) { //看输入的密码是否正确 if pwd != account.Pwd { fmt.Println("你输入的密码不正确") return } fmt.Printf("你的账户为=%v,余额为=%v\n", account.AccountNo, account.Balance) } func (account *Account) Operator(s2 string, pwd string, money float64) { switch s2 { case "存款": account.Deposite(money, pwd) case "取款": account.WithDraw(money, pwd) case "查询": account.Query(pwd) } } func main() { account := Account{ "gs1111", "6666", 100.0, } s2 := "" money := 0.0 fmt.Println("请输入查询操作类型:") fmt.Scanln(&s2) fmt.Println("请输入银行密码:") fmt.Scanln(&account.Pwd) fmt.Println("请输入操作金额:") fmt.Scanln(&money) account.Operator(s2, account.Pwd, money) fmt.Println("请输入查询操作类型:") fmt.Scanln(&s2) account.Operator(s2, account.Pwd, money) }
2.封装
封装就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作。
封装的好处:
1)隐藏实现细节
2)可以对数据进行验证,保证安全合理
如何实现封装:
1)对结构体中的属性进行封装
2)通过方法,包 进行封装
封装的实现步骤
1)将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似private)
2)给结构体所在的包提供一个工厂模式的函数,首字母大写。类似一个构造函数
3)提供一个首字母大写的Set方法(类似其它语言的public),用于对属性判断并赋值
func (var 结构体类型名) SetXxx(参数列表) (返回值列表) {
//加入数据验证的业务逻辑
var.字段 = 参数
}
4)提供一个首字母大写的Get方法(类似其它语言的public),用于获取属性的值
func (var 结构体类型名) GetXxx() {
return var.字段
}
示例1:
编写一个程序person.go,不能随便查看人的年龄,工资等隐私,并对输入的年龄薪水进行合理的验证
person.go
package person import "fmt" type person struct { Name string age int sal float64 } //只有Name是公有的 func NewPerson(name string) *person { return &person{ Name: name, } } func (p *person) SetPersonAge(age int) { if age > 0 && age < 150 { p.age = age } else { fmt.Println("年纪范围不正确") } } func (p *person) GetPersonAge() int { return p.age } func (p *person) SetPersonSal(sal float64) { if sal >= 3000 && sal <= 30000 { p.sal = sal } else { fmt.Println("薪水范围不对") } } func (p *person) GetPersonSal() float64 { return p.sal }
main.go
package main import ( "fmt" "gotest1/src/test/test47/person" ) func main() { p := person.NewPerson("王1") p.SetPersonAge(15) p.SetPersonSal(16567.1) fmt.Printf("%v的年龄为%v,工资为%v\n", p.Name, p.GetPersonAge(), p.GetPersonSal()) }
示例2:
1)创建程序,在model包中定义account结构体,在main函数中体会golang的封装性
2)account结构体要求有字段:账号(长度在6-10之间)、余额(必须>20)、密码(必须是六位数)
3)通过SetXxx给account的字段赋值。
4)在main函数中测试
model.go
package model import "fmt" type account struct { accountNo string pwd string balance float64 } //工厂模式-构造函数 func NewAccount() *account { return &account{} } //给no赋值 func (acn *account) SetNo(no string) { if len(no) <= 10 && len(no) >= 6 { acn.accountNo = no } else { fmt.Println("银行卡长度不正确") } } //返回no func (acn *account) GetNo() string { return acn.accountNo } //给pwd赋值 func (acn *account) SetPwd(pwd string) { if len(pwd) == 6 { acn.pwd = pwd } else { fmt.Println("密码长度不正确") } } //返回pwd func (acn *account) GetPwd() string { return acn.pwd } //给balance赋值 func (acn *account) SetBalance(balance float64) { if balance > 20 { acn.balance = balance } else { fmt.Println("余额不正确") } } //返回balance func (acn *account) GetBalance() float64 { return acn.balance }
main.go
package main import ( "fmt" "gotest1/src/test/test48/model" ) func main() { account := model.NewAccount() account.SetNo("18fsfs") account.SetPwd("664346") account.SetBalance(25) fmt.Println(account.GetNo(), account.GetPwd(), account.GetBalance()) }
3.继承
继续可以解决代码复用,让编程更靠近人类思维。
当多个结构体存在相同的属性(字段和方法时),可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法。
其他的结构体不需要重新定义这些属性(字段)和方法,只需要嵌套一个student匿名结构体即可。
也就是说:在Golang中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特效。
package main import "fmt" type Student struct { Name string Age int Score int } type Pupil struct { Student //继承Student结构体 heignt int } type Graduate struct { Student //继承Student结构体 } //获取信息 func (stu *Student) GetInfo() { fmt.Printf("%v的年龄是%v,分数是%v\n", stu.Name, stu.Age, stu.Score) } //放置分数 func (stu *Student) SetScore(sorce int) { stu.Score = sorce } //小学生特殊方法,获得身高 func (pupil *Pupil) GetHeigint() { fmt.Printf("%v的身高是%v\n", pupil.Name, pupil.heignt) } //小学生特殊方法,考试 func (pupil *Pupil) testing() { fmt.Println("小学生正在考试") } //大学生特殊方法,考试 func (graduate *Graduate) testing() { fmt.Println("大学生正在考试") } func main() { pupil := Pupil{} pupil.Name = "tom" pupil.Age = 8 pupil.heignt = 152 pupil.testing() pupil.SetScore(74) pupil.GetInfo() pupil.GetHeigint() graduate := Graduate{} graduate.Name = "Lili" graduate.Age = 24 graduate.testing() graduate.SetScore(98) graduate.GetInfo() }
3.1继承使用的细节
1)结构体可以使用嵌套匿名结构体所有的字段和方法,即首字母大写或者小写的字段、方法都可以使用。
2)匿名结构体字段访问可以简化,如b是B的实例,B继承A,A中有Name字段,则b.A.Name 的写法可以简化为b.Name
3)当结构体和匿名结构体有相同字段或者方法时,编译器采用就近访问原则,如希望访问匿名结构体的字段和方法,可以通过匿名结构体来区分。
4)结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则就报错。
5)如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合结构体的字段或方式时,必须带上结构体的名字。
B结构体中有有名结构体a A 所以不能直接用b.Name来调用,会报错,而应该带上有名结构体的名字 b.a.Name
type A struct { Name string } type B struct { a A } func main() { b := B{} b.a.Name = "tom" fmt.Println(b.a.Name) }
6)嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值。
package main import "fmt" type Goods struct { Name string Price float64 } type Brand struct { Name string Address string } type TV struct { Goods Brand } type TV2 struct { *Goods *Brand } func main() { tv1 := TV{Goods{"电视机001", 1132.2}, Brand{"天虹", "四川"}} tv2 := TV{ Goods{ Name: "电视机002", Price: 754.2, }, Brand{ Name: "乐视", Address: "香港", }, } fmt.Println(tv1) fmt.Println(tv2) tv3 := TV2{&Goods{"电视机003", 1132.2}, &Brand{"微软", "美国"}} tv4 := TV2{ &Goods{ Name: "电视机004", Price: 754.2, }, &Brand{ Name: "三星", Address: "韩国", }, } fmt.Println(*tv3.Goods, *tv3.Brand) fmt.Println(*tv4.Goods, *tv4.Brand) }
7)多重继承
如上面的tv2就是多重继承,继承的Goods和Brand中都有Name字段。若嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分。为了保持代码的简洁性,尽量不使用多重继承
浙公网安备 33010602011771号