1 封装
封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其它包只有通过被授权的操作方法,才能对字段进行操作。
封装好处:
- (1)隐藏实现细节
- (2)可以对数据进行验证,保证安全合理
如何体现封装
- (1)对结构体中的属性进行封装
- (2)通过
方法
、包
来实现封装
封装实现步骤
- (1)将结构体、字段(属性)的首字母小写(不能导出,其它包不能使用,类似private)
- (2)给结构体所在的包提供一个工厂模式的函数,首字母大写。类似一个构造函数
- (3)提供一个首字母大写的Set方法(类似其它语言的public),用于对属性判断并赋值
func (var 结构体类型名) SetXxx(参数列表) (返回值列表) {
// 加入数据验证的业务逻辑
var.字段 = 参数
}
- (4)提供一个首字母大写的Get方法(类似其它语言的public),用于获取属性的值
func (var 结构体类型名) GetXxx(){
// 返回值即可
return var.字段
}
在Golang开发中并没有强调封装,Golang本身对面向对象的特性做了简化。
1.1 实例1
自定义包
package model
import "fmt"
type person struct {
Name string
age int
sal float64
}
// 设置一个工厂函数
func NewPerson(name string) *person {
return &person{
Name : name,
}
}
// 创建一个设置年龄的方法
func (p *person) SetAge(age int) {
if age > 0 && age < 150 {
p.age = age
} else {
fmt.Println("您输入的年龄有误")
}
}
// 获取年龄
func (p *person) GetAge() int {
return p.age
}
// 创建一个设置薪水的方法
func (p *person) SetSal(sal float64) {
if sal > 3000 && sal < 30000 {
p.sal = sal
} else {
fmt.Println("您输入的薪水有误")
}
}
// 获取薪水
func (p *person) GetSal() float64 {
return p.sal
}
引用自定义包
package main
import (
"fmt"
"go_code/OOPobject/encapsulation/model"
)
func main() {
p := model.NewPerson("Smith")
p.SetAge(19)
p.SetSal(6000)
fmt.Println(p)
fmt.Println("名字=", p.Name, "年龄=", p.GetAge(), "薪水=", p.GetSal())
//&{Smith 19 6000}
//名字= Smith 年龄= 19 薪水= 6000
}
1.2 实例2
要求:
- (1)Account结构体字段:账号(长度在6~10之间)、余额(必须>20)、密码(必须是六位)
- (2)通过SetXxx和GetXxx方法给字段赋值
- (3)main函数中测试
自定义包
package model
import (
"fmt"
)
// 在model包中定义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 < 20 {
fmt.Println("余额不足...")
return nil
}
return &account{
accountNo: accountNo,
pwd: pwd,
balance: balance,
}
}
func (a *account) SetPwd(pwd string) {
if len(pwd) != 6 {
fmt.Println("密码长度有误...")
return
}
a.pwd = pwd
}
func (a *account) GetPwd() string {
return a.pwd
}
引用自定义包
package main
import (
"fmt"
"go_code/OOPobject/encapsulateprise/model"
)
func main() {
a := model.NewAccount("建设银", "123456", 40)
a.SetPwd("123456")
fmt.Println(a.GetPwd())
}
2 继承
-
继承可以解决代码复用,让变成更加靠近人类的思维
-
当多个结构体存在
相同的属性(字段)和方法
时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法。 -
其它的结构体不需要重新定义这些属性和方法,只需要
嵌套一个匿名结构体
即可。 -
在Golang中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性。
继承的语法
type Goods struct{
Name string
Price int
}
type Book struct{
Goods // 这里就是嵌套匿名结构体Goods
Writer string
}
// 使用Goods中的属性最简单的形如 Book.Goods.Name
2.1 实例1
比如有一个学生管理系统,传统情况下需要定义许多个不同年级的结构体来区分不同年级学生的姓名成绩及做的事情等,这样用继承就可以声明一个Student的匿名结构体,被不同年级的结构体所嵌套使用
package main
import (
"fmt"
)
// 定义一个Student匿名函数,供不通年级的struct嵌套使用
type Student struct{
Name string
Age int
Score float64
}
// 定义一个Student的方法SetScore
func (stu *Student) SetScore(score float64){
stu.Score = score
}
// 定义一个Student的方法ShowInfo
func (stu *Student) ShowInfo() {
fmt.Printf("学生姓名=%v 年龄=%v 成绩=%v \n", stu.Name, stu.Age, stu.Score)
}
// 定义大学生的结构体
type Graduate struct{
Student
}
// 定义大学生的方法
func (p *Graduate) testing(){
fmt.Println("大学生正在考试中...")
}
// 定义小学生的结构体
type Pupil struct{
Student
}
// 定义小学生的方法
func (p *Pupil) testing(){
fmt.Println("小学生正在考试中...")
}
func main() {
// 定义一个graduate变量
graduate := &Graduate{}
graduate.Student.Name = "tom"
graduate.Student.Age = 18
graduate.SetScore(70)
graduate.testing()
graduate.ShowInfo()
// 定义一个pupil变量
pupil := &Pupil{}
pupil.Student.Name = "Marry"
pupil.Student.Age = 20
pupil.SetScore(80)
pupil.testing()
pupil.ShowInfo()
/* 输出信息如下:
大学生正在考试中...
学生姓名=tom 年龄=18 成绩=70
小学生正在考试中...
学生姓名=Marry 年龄=20 成绩=80
*/
*/
}
// 代码的扩展性和维护性提高了
// 代码的复用性提高了
2.2 details
- (1)结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法,都可以使用
- (2)匿名结构体字段访问可以简化
pupil.Student.Name = "Marry" // ==> pupil.Name = "Marry"
pupil.Student.Age = 20 // ==> pupil.Age = 20
- (3)当结构体和匿名结构体有相同的字段或者方法时,编译器采用
就近访问原则
,如果希望访问匿名结构体的字段和方法,可以通过指定具体匿名结构体名来区分
package main
import (
"fmt"
)
// 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则,
// 如果希望访问匿名结构体的字段和方法,可以通过指定具体匿名结构体名来区分
type A struct {
Name string
Age int
}
func (a *A) SayHello() {
fmt.Println("A SayHello = ", a.Name)
}
// 定义一个B结构体,嵌套A结构体
type B struct {
A
Name string
}
func (a *B) SayHello() {
fmt.Println("B SayHello = ", a.Name)
}
func main() {
b := B{}
b.Name = "jack"
b.Age = 20
b.SayHello() // B SayHello = jack
b.A.SayHello() //A SayHello = 会输出空串
}
- (4)结构体嵌入两个或多个匿名结构体,如果两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须
明确指定匿名结构体名字
,否则编译会报错
// 下边三个结构体,C结构体引用A和B结构体,此时若C指定Name但是没有明确嵌套的哪个结构体,那么就会报错
type A struct{
Name string
Age int
}
type B struct{
Name string
Scores float64
}
type C struct{
A
B
}
- (5)如果一个struct嵌套了一个有名的结构体,这种模式就是组合,如果是组合关系,那么再访问组合的结构体的字段或方法时,必须带上结构体的名字
// 形如:
type A struct{
Name string
Age int
}
type C struct{
a A // 组合关系
}
- (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() {
// tv := Tv{ Goods{"电视机", 1000}, Brand{"海尔", "北京"} }
tv := Tv{
Goods{
Name : "电视机",
Price : 1000,
},
Brand{
Name : "海尔",
Address : "北京",
},
}
fmt.Println("tv = ", tv) // tv = {{电视机 1000} {海尔 北京}}
tv2 := Tv2{
&Goods{
Name : "电视机003",
Price : 2000,
},
&Brand{
Name : "TCL",
Address : "上海",
},
}
fmt.Println("tv = ", *tv2.Goods, *tv2.Brand) // tv = {电视机003 2000} {TCL 上海}
}
- (7)当结构体的匿名字段是基本数据类型时也可以正常访问的
package main
import "fmt"
type Monster struct{
Name string
Age int
}
type E struct{
Monster
int // 定义基本数据类型
n int // 注意在此结构体中只能有一个int,要想有多个请给数据类型则必须给此int字段指定名字
}
func main() {
var e E
e.Name = "Tom"
e.Age = 20
e.int = 40
fmt.Println("e=", e)
}
2.3 多重继承
- 如果一个struct嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承。
- 如果嵌入的匿名结构体有相同的字段名或者方法名,在访问时,则需要通过匿名结构体名来区分
- 为保证代码的整洁性,尽量不要使用多重继承
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() {
// tv := Tv{ Goods{"电视机", 1000}, Brand{"海尔", "北京"} }
tv := Tv{
Goods{
Name : "电视机",
Price : 1000,
},
Brand{
Name : "海尔",
Address : "北京",
},
}
fmt.Println("tv = ", tv) // tv = {{电视机 1000} {海尔 北京}}
tv2 := Tv2{
&Goods{
Name : "电视机003",
Price : 2000,
},
&Brand{
Name : "TCL",
Address : "上海",
},
}
fmt.Println("tv = ", *tv2.Goods, *tv2.Brand) // tv = {电视机003 2000} {TCL 上海}
}