package model
/**
* @Description:结构体的定义与访问函数的准备
* @Author: guai
* @Date:2020/2/22 19:55
**/
//当声明的结构体名为小写时,在包外无法直接使用,可以使用工厂模式解决
//可以理解为,在golang中使用命名的大小写控制访问权限,
//大写可在包外访问,小写只能在包内访问
type student struct {
Name string
//当变量名首位小写时也无法在保外访问此时可同通过本类的方法来间接访问
core float64
}
//通过学生工厂函数 ,返回结构体指针类型的变量
func StudentFactory(n string, s float64) *student {
return &student{
Name: n,
core: s,
}
}
//这像极了爱情 不,像极了java中的属性封装啊
func (stu *student) GetAge() float64 {
return stu.core
}
在main包中创建文件
package main
import (
"fmt"
//导入model包
"go_code/project1/model"
)
/**
* @Description:工厂模式
* @Author: guai
* @Date:2020/2/22 18:42
**/
func main() {
//golnag中的工厂模式是通过函数实现的而在java中则是通过类实现的
//通过model包中的StudentFactory函数初始化student变量
student := model.StudentFactory("guai", 12.2)
fmt.Println(student)
fmt.Println(student.Name, "的成绩为:", student.GetAge())
}
package main
import (
"fmt"
"time"
)
/**
* @Description: 抽象出一个账户模型(结构体)并使用
* @Author: guai
* @Date:2020/2/22 20:02
**/
type account struct {
name string
password string
money float64
//用来保存账户金额变动记录
record []string
}
//创建账户
func (acc *account) createAccount(name string, password string) {
acc.name = name
acc.password = password
fmt.Println("尊敬的", name, "用户您的账户创建成功,每个账户的只能只用一次,当退出时账户将被销毁")
}
//存钱
func (acc *account) addMoney(money float64) {
acc.money += money
fmt.Println("您已存入金额:", money, "目前余额为:", acc.money)
acc.addRecord("您与" + time.Now().Format("2006-01-02 15:04:05") + "存入了" + fmt.Sprintf("%f", money))
}
//取钱
func (acc *account) lessMoney(money float64) {
if acc.money >= money {
acc.money -= money
fmt.Println("您已取出金额:", money, "目前余额为", acc.money)
acc.addRecord("您与" + time.Now().Format("2006-01-02 15:04:05") + "取出了" + fmt.Sprintf("%f", money))
} else {
fmt.Println("余额不足")
}
}
//保存记录
func (acc *account) addRecord(rec string) {
acc.record = append(acc.record, rec)
}
func main() {
var acc account
//创建账户
acc.createAccount("guai", "123")
//存钱
acc.addMoney(123.3)
//打印记录
fmt.Println("打印记录:", acc.record)
//取钱
acc.lessMoney(123.3)
fmt.Println("打印记录,", acc.record)
}
3.1、特点:
3.2、如何实现封装
3.3、实现步骤
例:
在model包中创建文件
package model
import "fmt"
/**
* @Description:以认为对象抽象出的模型
* @Author: guai
* @Date:2020/2/22 21:38
**/
type person struct {
Name string
age int
salary float64
}
//工厂模式的函数,类似构造
func PersonFactory(name string) *person {
return &person{
Name: name,
}
}
func (per *person) SetAge(age int) {
if age > 0 && age < 150 {
per.age = age
} else {
fmt.Println("这年龄恐怕不对呢")
}
}
func (per *person) GetAge() int {
return per.age
}
//设置薪水
func (per *person) SetSalary(sal float64) {
if sal >= 300 && sal <= 30000 {
per.salary = sal
} else {
fmt.Println("这工资恐怕不对呢")
}
}
//获取薪水
func (per *person) GetSalary() float64 {
return per.salary
}
在main包中创建
package main
import (
"fmt"
//导入model包
"go_code/ObjectOriented/model"
)
/**
* @Description: 将抽象的人具体为工人
* @Author: guai
* @Date:2020/2/22 21:50
**/
func main() {
worker := model.PersonFactory("cabbage")
worker.SetAge(18)
worker.SetSalary(10000)
fmt.Printf("%v 今年 %d岁 工资为 %f", worker.Name, worker.GetAge(), worker.GetSalary())
}
作用:
1)提高代码复用性
2)代码的扩展性和可维护性提高了
若将教师和学生都抽象出来他们都有名字、年龄等相同字段,此时可以抽象一个人,有名字、年龄等,然后让学生、老师啥的可以继承人
4.1、在golang中可以通过嵌套匿名结构体来实现继承
例:
package main
import "fmt"
/**
* @Description:通过嵌套匿名结构体实现继承
* @Author: guai
* @Date:2020/2/22 22:37
**/
type person struct {
name string
age int
}
func (per *person) say() {
fmt.Println("我是", per.name, " 我今年:", per.age, "岁")
}
type son struct {
//昵称,作为儿子在家中有自己的昵称
nickname string
}
func (s *son) say() {
fmt.Println("我是爸爸的好儿子:", s.nickname)
}
type student struct {
//通过嵌套匿名结构体实现继承
person
//结构体中可以嵌套多个匿名结构体 当嵌套的匿名结构体由同名的方法在调用时需要指定是哪个结构体绑定的方法
son
//结构体和匿名结构体中可以由相同字段,在访问时如果使用
//student.name 编译器会采用就近访问原则,访问的时student中的name,如果想要访问
//person中的name需要使用student.person.name指定具体访问的那个name
name string
}
func main() {
var stu student
//直接使用person中的字段和方法
stu.name = "guai"
stu.person.name = "人"
stu.son.nickname = "乖"
//因不存在重名字段 可直接引用
stu.age = 12
stu.person.say()
//指定那个结构体的say方法
stu.son.say()
//在创建结构体变量时也可以,直接指定各个匿名结构体字段的值
stu1 := student{person{"人", 18}, son{"小白菜"}, "cabbage"}
fmt.Println("stu1:", stu1)
}
结果:
4.2、当一个struct嵌套一个有名结构体,这种模式称为组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字如下:
func main(){
b := BB{AA{"大白菜"}}
b.a.say()
}
//组合
type AA struct {
name string
}
func (a *AA) say() {
fmt.Println("我们是组合AABB我们是", a.name)
}
type BB struct {
//嵌套有名结构体
a AA
}
4.3、多重继承
在golang中支持多重继承,实现方法:在结构体中嵌套多个匿名结构体,那么该结构体就可以直接访问嵌套的匿名结构体的字段和方法,从而实现多重继承。
在golang中多态的特性主要通过接口来体现,在了解多态之前,我们先来看接口
5.1、 接口可以用来制定一个统一的规范例如:手机的充电接口,当所有厂商都遵循一个统一的规范时,手机的usb线就可以共用。
在phone结构体绑定start()和stop()方法之前phone没有实现usb接口,从上面的图中可以看出,phone并没有实现接口的图标,当phone绑定并实现了start()和stop()后可以看到下图中,phone出现了实现接口的图标,由此可知在golnag中只要绑定并实现接口的所有方法,就相当于实现了该接口
码:
package main
import "fmt"
/**
* @Description: 面向对象接口
* @Author: guai
* @Date:2020/2/23 11:05
**/
type Usb interface {
start()
stop()
}
type phone struct {
}
//phone通过实现usb接口的方法 实现接口
func (p phone) start() {
fmt.Println("手机开始工作了")
}
func (p phone) stop() {
fmt.Println("手机停止工作")
}
type camera struct {
}
//相机通过实现usb接口的方法实现接口
func (c camera) start() {
fmt.Println("相机开始工作了")
}
func (c camera) stop() {
fmt.Println("相机停止工作")
}
type computer struct {
}
//usb变量会根据传入的实参,来判断到底phone还是camera
func (c computer) working(usb Usb) {
usb.start()
usb.stop()
}
func main() {
com := computer{}
ph := phone{}
ca := camera{}
//类似java中的向上转型
com.working(ph)
com.working(ca)
}
5.3、接口的应用场景
5.4、接口使用注意事项和细节
1)接口本身不能创建实例,但可以指向一个实现了接口的自定义类型的变量(实例)
2)接口中的所有方法没有方法体
3)一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
4)在golang中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型(不只是struct)实现了该接口
例:
//非struct的自定义类型实现 接口
func mian(){
var myint myInt = 12
myint.isay()
}
//定义接口
type say interface {
isay()
}
//定义一个非struct类型的自定义类型 本质上该类型仍然是int类型相当与给int起别名
//挡在golang中认为这属于一个自定义类型
type myInt int
//实现接口中的方法
func (myint *myInt) isay() {
fmt.Println("我是整型:", *myint)
}
结果:
5)golang接口中不能有变量
6)一个自定义类型可以实现多个接口,但必须实现所有的方法
7)一个接口(如A)可以继承多个别的接口(如B、C、),如果,要实现A则要实现A、B、C中的所有方法(接口间的继承和结构体的继承方式一样 通过嵌套匿名接口实现)
例:
//接口继承接口测试
func main(){
tm := testMultiple{}
//通过testMultiple实例化接口
var e E = tm
e.eSay()
}
//3、
type C interface {
cSay()
}
type D interface {
dSay()
}
type E interface {
C
D
eSay()
}
type testMultiple struct {
}
//通过实现CDE中所有方法 来实现e接口
func (tm testMultiple) cSay() {
fmt.Println("i am c")
}
func (tm testMultiple) dSay() {
fmt.Println("i am d")
}
func (tm testMultiple) eSay() {
tm.cSay()
tm.dSay()
}
结果:
8)interface类型默认是一个指针(引用类型),如果没有对interface初始化就是用结果为nil
9)空接口 interface{}没有任何方法,多有类型都默认实现了空接口,可以通过空接口来接收任意变量
5.5、接口应用练习
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
/**
* @Description:接口应用练习
* @Author: guai
* @Date:2020/2/23 12:09
**/
//实现对Hero结构体切片的排序:sort.Sort(data Interface)
type Hero struct {
Name string
Age int
}
type HeroSlice []Hero
//实现Interface接口
func (hs HeroSlice) Len() int {
return len(hs)
}
//按年龄从小到大排序
func (hs HeroSlice) Less(i, j int) bool {
return hs[i].Age < hs[j].Age
}
//交换
func (hs HeroSlice) Swap(i, j int) {
//temp:=hs[i]
//hs[i]=hs[j]
//hs[j]=temp
//下面的语句和上面三条语句功能一样 即实现交换
hs[i], hs[j] = hs[j], hs[i]
}
func main() {
var heros HeroSlice
rand.Seed(time.Now().UnixNano())
for i := 0; i < 10; i++ {
heros = append(heros, Hero{
Name: fmt.Sprintf("hero|%d", i),
Age: rand.Intn(100),
})
}
fmt.Println("排序前的切片:")
for _, hero := range heros {
fmt.Println(hero)
}
//排序
sort.Sort(heros)
fmt.Println("排序后的切片:")
for _, hero := range heros {
fmt.Println(hero)
}
}
来看一下sort.Sort(data Interface)方法以及Interface接口长啥样咩
结果:
5.6、实现接口VS继承
1)当A结构体继承了B结构体,那么A结构体就可以直接使用B结构体的字段和方法,更有利于代码复用
2)当A结构体需要功能扩展时,同时不希望破坏继承关系,则可以实现某个接口即可,我们认为接口实现是为继承机制的一种补充
5.7、接口搞得差不多了,我们来看看——多态
5.7.1、基本介绍
变量(实例)具有多种形态称为多态,早golang中多态特征是通过接口实现的,可以按照统一的接口来调用不同的实现。这是接口变量就会呈现不同的形态
package main
import "fmt"
/**
* @Description: 面向对象接口
* @Author: guai
* @Date:2020/2/23 11:05
**/
//1、
type Usb interface {
start()
stop()
}
type phone struct {
name string
}
//phone通过事项usb接口的方法 实现接口
func (p phone) start() {
fmt.Println("手机开始工作了")
}
func (p phone) stop() {
fmt.Println("手机停止工作")
}
type camera struct {
name string
}
//相机通过实现usb接口的方法实现接口
func (c camera) start() {
fmt.Println("相机开始工作了")
}
func (c camera) stop() {
fmt.Println("相机停止工作")
}
type computer struct {
}
//usb变量会根据传入的实参,来判断到底phone还是camera
//此处即为通过实现接口的自定义类型实例化接口
func (c computer) working(usb Usb) {
usb.start()
usb.stop()
}
func main() {
//接口数组 保存两种不同类型的结构体
var usb [3]Usb
usb[0]=phone{"某米"}
usb[1]=phone{"某为"}
usb[2]=camera{"某康"}
fmt.Println(usb)
}
结果:
虽然是两种不同类型的结构但都实现了usb接口,可以用usb接口数组保存手机和相机两种类型的结构体
6.1、由具体需求,引出类型断言,当多个类型(A、B)实现了同一个接口(C)并同过变量实例化了接口(c:=A{},c1:=B{}),若此时想要将接口(c、c1)再赋给一个变量(var a A=c ,var b=c1)时,需要判断(c,c1)是通过那个类型(A、B)实例化的,而不可以出现(var a A=c1 ,var b B=c)此时就需要使用类型断言(和java中的interfaceof)
具体如下:
6.2、基本介绍
类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要类型断言
码:
func main() {
//定义一个空接口变量 用来接收任意类型
var x interface{}
var b2 float64=1.1
x=b2
//类型断言
y:=x.(float64)
fmt.Printf("y的类型是%T 值是%v",x,y)
}
注意:在进行断言是,如果类型不匹配就会报panic(恐慌),因此进行类型断言使,要确保原来的空接口指向的就是断言的类型。
如何在进行断言时,带上检测机制,如果成功就ok,否则也不要报panic
func main(){
//定义一个空接口变量 用来接收任意类型
var x interface{}
var b2 float64=1.1
x=b2
//带有判断的类型断言 这相当于一个函数调用返回两个值一个是转换y表示转成的具体类型
//ok表示转换结果,成功为true失败为false,当失败时,z为该类型的默认值此时为0
if z,ok:= x.(float32); ok{
fmt.Println("convert success")
fmt.Printf("y的类型是%T 值是%v", z, z)
} else {
fmt.Println("convert fail! ok:",ok," y:",z)
}
}
6.3、断言应用
注意:此处的 x.(type)只能用在switch中
文中图片部分来自Go语言-尚硅谷_韩顺平_Go语言核心编程.pdf
如需删除请留言私聊
跪谢