一、开发步骤
1、声明(定义)结构体,并确定结构体名
2、编写结构体字段
3、编写结构体方法
二、应用
1、
package main
import (
"fmt"
_ "fmt"
)
type Student struct {
name string
gender string
age int
id int
score float64
}
func (student *Student) say() {
fmt.Printf("name:%v,gender:%v,id:%v,score:%v\n",student.name,student.gender,student.age,student.id,student.score)
}
func main() {
stu :=Student{"tom","male",18,1,100.00}
(&stu).say()
}
2、
package main
import (
"fmt"
_ "fmt"
)
type Box struct {
len float64
width float64
hight float64
}
func (box Box) volum() float64 {
v := box.len * box.hight * box.width
return v
}
func main() {
var box Box
box.width = 1.00
box.hight = 2.00
box.len = 3.00
v := box.volum()
fmt.Printf("v=%v",v)
}
3、
package main
import (
"fmt"
_ "fmt"
)
type Visitor struct {
Name string
Age int
}
func (visitor *Visitor) howmuch() {
var money float64 = 0
if visitor.Age >60||visitor.Age<8{
fmt.Print("请勿游玩")
}
if visitor.Age >18{
money = 20
fmt.Printf("%v",money)
}else {
fmt.Printf("%v",money)
}
}
func main() {
var visitor Visitor
for {
fmt.Print("输入姓名:")
_, _ = fmt.Scan(&visitor.Name)
if visitor.Name == "n"{
fmt.Print("退出\n")
break
}
fmt.Print("输入年龄:")
_, _ = fmt.Scan(&visitor.Age)
}
visitor.howmuch()
}
三、工厂模式
1、使用场景如下:
package model
type student struct{
...
}
当上面结构体名是小写,但是又想在其他包里面使用到student结构体时使用工厂模式(若是大写,引包后就可以直接用了)
package model
type student struct { //小写
Name string //大写
score float64 //小写
}
//工厂模式
func NewStudent(n string,s float64) *student {
return &student{ //返回student指针
Name: n,
score: s,
}
}
func (s *student) GetScore() float64 { //那么写一个对外的方法返回值就可以了
return (*s).score
}
package main
import (
"fmt"
_ "fmt"
"remoteProjects/基本语法/Chapter08/demo03/model"
_ "remoteProjects/基本语法/Chapter08/demo03/model"
)
func main() {
var stu = model.NewStudent("tom",98.8)
fmt.Println(*stu)
fmt.Printf("name:%v score:%v",stu.Name,stu.GetScore())
}
四、面向对象思想——抽象
1、将一类事物共有的属性(字段)和行为(方法)提取出来,形成一个物理模型
2、银行账户案例
package main
import (
"fmt"
_ "fmt"
)
type Account struct {
Accountnum string
Psw string
Balance float64
}
func (account *Account) Deposite (money float64, pwd string){
//看输入密码是否正确
if pwd != account.Psw{
fmt.Printf("erro password\n")
return
}
if money <=0{
fmt.Printf("error money\n")
return
}
account.Balance += money
fmt.Printf("success\n")
}
func (account *Account) Withdraw (money float64, pwd string){
//看输入密码是否正确
if pwd != account.Psw{
fmt.Printf("erro password\n")
return
}
if money <=0 || money>account.Balance{
fmt.Printf("error money\n")
return
}
account.Balance -= money
fmt.Printf("success\n")
}
func (account Account) Query(pwd string) {
if pwd != account.Psw{
fmt.Printf("erro password\n")
return
}
fmt.Printf("你的账号余额为%v\n",account.Balance)
}
func main() {
account :=Account{
Accountnum: "1",
Psw: "666666",
Balance: 100,
}
account.Query("666666")
account.Deposite(666,"666666")
account.Withdraw(1000,"666666")
account.Query("666666")
}
五、面向对象三大特征
1、封装
1.说明
(1)封装就是把抽象出来的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只能通过被授权的操作(方法),才能对字段进行操作
(2)golang中封装不是很严格
例如:对电视机的操作
2.好处
(1)隐藏实现细节
(2)可以对数据进行验证,保证安全合理
3.应用
(1)对结构体中的属性进行封装
(2)通过方法,包实现封装
4.实现步骤
(1)将结构体、字段(属性)首字母进行小写,其他包就不能访问了,类似private
(2)给结构体所在包提供一个工厂模式的函数,首字母大写,类似一个构造函数
(3)提供一个首字母大写的Set方法(类似其他语言的public),用于对属性判断并赋值
func (var 结构体类型名) Setxxx(参数列表)(返回值列表){
//加入业务判断逻辑
}
(4)提供一个首字母大写的Get方法(类似其他语言的public),用于获取属性的值
func (var 结构体类型名) GetXXX(){
return var 字段
}
5.快速入门
(1)设计一个程序(Person.go),不能随便查看人的年龄、工资等隐私,并对输入的年龄进行合理的验证
main.go
package main
import (
"fmt"
_ "fmt"
"remoteProjects/基本语法/Chapter08/fengzhuang/model"
_ "remoteProjects/基本语法/Chapter08/fengzhuang/model"
)
func main() {
p:=model.NewPerson("smith")
p.SetAge(110)
p.SetSal(6000)
fmt.Print(*p)
}
person.go
package model
import "fmt"
type person struct {
Name string
age int //不可直接访问
sal float64
}
//工厂模式,相当于构造函数
func NewPerson(name string) *person {
return &person{
Name: name,
}
}
//为了访问age和sal,编写一对Setxxx的方法和Getxxx的方法
func (p *person) SetAge(age int) {
if age >0 &&age<150{
p.age =age
}else {
fmt.Printf("年龄错误\n")
}
}
func (p *person) GetAge() int {
return p.age
}
func (p *person) SetSal(sal float64) {
if sal >3000 &&sal<30000{
p.sal =sal
}else {
fmt.Printf("薪水错误\n")
}
}
func (p *person) GetSal() float64 {
return p.sal
}
(2)练习
main.go
package main
import (
"fmt"
_ "fmt"
"remoteProjects/基本语法/Chapter08/fzdemo/model"
)
import _ "remoteProjects/基本语法/Chapter08/fzdemo/model"
func main() {
account := model.NewAccount("1")
account.SetPwd("666666")
account.SetBalance(1000.01)
money :=account.GetBalance("666666")
fmt.Printf("money=%v",money)
}
account.go
package model
import (
"fmt"
_ "fmt"
)
type account struct {
AccountNum string
pwd string
balance float64
}
func NewAccount(acnum string) *account {
return &account{
AccountNum: acnum,
}
}
func (account *account) SetPwd(pwd string) {
if len(pwd)<6 || len(pwd)>10{
fmt.Printf("wrong pwd")
}
(*account).pwd = pwd
}
func (account *account) SetBalance (money float64) {
if money <=20{
fmt.Printf("wrong money")
}
(*account).balance += money
}
func (account *account) GetBalance(pwd string) float64 {
if pwd != (*account).pwd{
fmt.Printf("wrong pwd")
}
return (*account).balance
}
2、继承
1.引言:
看个学生的考试系统程序,提出代码复用的问题,解决冗余代码,当多个结构体有相同的属性时,可以使用继承
2.关系示意图
3.在Golang中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和结构体,从而实现了继承特性
(1)使用举例
type Goods struct{
Name string
Price string
}
type Book struct{
Goods //匿名结构体
Writer string
}
(2)案例说明
package main
import (
"fmt"
_ "fmt"
)
type Student struct {
Name string
Age int
Score float64
}
//显示他的成绩
func (s *Student) ShowInfo(){
fmt.Printf("学生名字:%v,年龄%v,成绩%v\n",s.Name,s.Age,s.Score)
}
//显示他的成绩
func (s *Student) SetScore(score float64) {
s.Score = score
}
//显示他的状态
func (s *Student) testing() {
fmt.Print("testing\n")
}
type Pupil struct {
Student
}
type Graduate struct {
Student
}
func main() {
//使用方法一
//小学生
pupil := &Pupil{}
pupil.Student.Name = "tom"
pupil.Student.Age = 8
pupil.Student.testing()
pupil.Student.SetScore(70)
pupil.Student.ShowInfo()
//大学生
graduate := &Graduate{}
graduate.Student.Name = "mary"
graduate.Student.Age = 8
graduate.Student.testing()
graduate.Student.SetScore(70)
graduate.Student.ShowInfo()
}
4.继承的细节
(1)结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段,方法都可以用
(2)匿名结构体字段访问可以简化:
如:
func main() {
//使用方法一
//小学生
pupil := &Pupil{}
pupil.Student.Name = "tom"
pupil.Student.Age = 8
pupil.Student.testing()
pupil.Student.SetScore(70)
pupil.Student.ShowInfo()
}
可以变成:
func main() {
//使用方法一
//小学生
pupil := &Pupil{}
pupil.Name = "tom"
pupil.Age = 8
pupil.testing()
pupil.SetScore(70)
pupil.ShowInfo()
}
因为编译器会先看pupil有没有这个方法,如果有则直接调用,如果没有就看student里面有没有,有则调用,没有就看是否还有嵌入结构体就再继续找,找到了就调用,最后还没有就报错
(3)当结构体和匿名结构体有相同的字段或者方法时,编译器就采用就近访问原则,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分
(4)结构体嵌入两个(或者多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须指定匿名结构体名字,否则编译报错
(5)如果一个struct结构体嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问结构体的字段或者方法时,就必须带上结构体的名字
type A struct{
Name string
Age int
}
type C struct{
a A
}
func main(){
var c C
c.A.Name = "tom" //像这样使用
}
5.多重继承
如一个struct嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现多重继承(不建议使用)
6.综合案例
package main
import (
"fmt"
_"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: Goods{"电视机",7000},
Brand: Brand{"海尔","山东"},
}
fmt.Print(tv1)
tv2 := TV{ Goods{"电视机",8000},Brand{"长虹","北京"}}
fmt.Print("\n")
fmt.Print(tv2)
tv3 :=TV2{
Goods: &Goods{
Name: "电视",
Price: 6000,
},
Brand: &Brand{
Name: "创维",
Address: "河南",
},
}
fmt.Print("\n",tv3)
fmt.Print("\n",tv3.Goods,tv3.Brand)
fmt.Print("\n",*tv3.Goods,*tv3.Brand)
}
5.接口和继承的关系
Tips:
如果这里并没有学过接口的同学,可以先看看下一章关于接口的讲解再回来看接口与继承的关系 Chapter013 golang接口
(1)代码案例
package main
import (
"fmt"
_ "fmt"
)
type Monkey struct {
Name string
}
func (this *Monkey) climbing() {
fmt.Print(this.Name,"生来会爬树..\n")
}
type LittleMonkey struct {
Monkey
}
type BirdAble interface {
Flying()
}
func (this *LittleMonkey) Flying() {
fmt.Print(this.Name,"通过学习,会飞翔..\n")
}
type FishAble interface {
Swimming()
}
func (this *LittleMonkey) Swimming() {
fmt.Print(this.Name,"通过学习,会游泳..\n")
}
func main() {
monkey := LittleMonkey{Monkey{
Name:"悟空",
},}
monkey.climbing()
//小猴子继承猴子特性
//对小猴子特殊地拓展————》接口(对继承的补充)
monkey.Flying()
monkey.Swimming()
}
(2)关系
①接口可以在不破坏继承关系的前提下拓展功能,即:实现接口是对继承机制的补充
②接口和继承解决的问题不同
1)继承的价值主要在于:解决代码的 复用性和可维护性
2)接口的价值主要在于:设计,设计好各种规范(方法),让其他自定义类型去实现这些方法
③接口比继承更加灵活
接口比继承更加灵活,继承是满足 is - a的关系,而接口只需要满足like -a的关系
④接口在一定程度上实现 代码解耦
3、多态
1.说明
变量(实例)具有多种形态。面向对象第三大特征,在Go语言中,多态是通过接口来实现的。可以按照统一接口来调用不同的实现。这时接口变量就呈现了不同的形态
2.案例入门
在接口一章中(Chapter013 golang接口),我们使用到了Usb usb ,既可以接受手机变量,又可以接受相机变量,就体现了Usb接口 多态特性。
3.接口体现多态特征
(1)多态参数 :在前面的Usb接口案例,Usb usb,既可以接受手机变量,又可以接受相机变量,就体现了 Usb接口 多态
(2)多态数组:
案例:(断言时细讲) 给Usb数组中,存放Phone结构体和Camera结构体变量,Phone还有一个特有的方法Call(),请遍历Usb数组,如果是Phone变量,除了调用Usb接口外,还需要调用Phone特有方法Call
package main
import (
"fmt"
_ "fmt"
)
type Usb interface {
Start()
Stop()
}
type Phone struct {
Name string
}
func (p Phone) Start() {
fmt.Printf("手机开始工作\n")
}
func (p Phone) Stop() {
fmt.Printf("手机停止工作\n")
}
type Camera struct {
Name string
}
func (c Camera) Start() {
fmt.Printf("相机开始工作\n")
}
func (c Camera) Stop() {
fmt.Printf("相机停止工作\n")
}
type Computer struct {
Name string
}
func (c Computer) Working(usb Usb) {
usb.Start()
usb.Stop()
}
func main() {
//定义一个Usb接口数组,可以存放Phone 和 Camera
var UsbArr [3]Usb
fmt.Print(UsbArr,"\n")
UsbArr[0] = Phone{"vivo",}
UsbArr[1] = Phone{"huawei",}
UsbArr[2] = Camera{"nikang"}
fmt.Print(UsbArr,"\n")
}
4.类型断言
(1)案例说明
package main
import (
"fmt"
_ "fmt"
)
type Point struct {
x int
y int
}
func main() {
var a interface{}
var point Point = Point{1,2}
a = point
//如何将a赋给一个Point变量 ————>断言
var b Point
b = a.(Point) //**************************************
fmt.Print(b)
}
b = a.(Point)
作用:表示判断a是否是指向Point类型的变量,如果是,就转成Point类型并赋给b变量,否则报错(即:a本来就要指向b)
func main() {
//类型断言的其他类型
var x interface{}
var c float32 = 1.1
x = c
y:=x.(float32)
fmt.Printf("y类型是%T",y)
}
如果改成
y:=x.(float64)
报错
(2)带检查的断言机制
如何在进行断言时,带上检测机制,如果成功就ok,否则就不需要报panic错误
//带检测的类型断言
var z interface{}
var d float32 = 1.1
z =d
f,ok :=z.(float64)
if ok{
fmt.Print("成功\n")
}else {
fmt.Print("失败 继续执行\n")
}
fmt.Printf("f类型是%T\n",f)
(3)类型断言最佳实践
案例1: 给Usb数组中,存放Phone结构体和Camera结构体变量,Phone还有一个特有的方法Call(),请遍历Usb数组,如果是Phone变量,除了调用Usb接口外,还需要调用Phone特有方法Call()
package main
import (
"fmt"
_ "fmt"
)
type Usb interface {
Start()
Stop()
}
type Phone struct {
Name string
}
func (p Phone) Start() {
fmt.Printf("手机开始工作\n")
}
func (p Phone) Stop() {
fmt.Printf("手机停止工作\n")
}
func (p Phone) Call() {
fmt.Printf("手机 打电话\n")
}
type Camera struct {
Name string
}
func (c Camera) Start() {
fmt.Printf("相机开始工作\n")
}
func (c Camera) Stop() {
fmt.Printf("相机停止工作\n")
}
type Computer struct {
Name string
}
func (c Computer) Working(usb Usb) {
usb.Start()
//如果是指向Phone结构体变量,那么再来一个Call()方法
//类型断言
if phone , ok := usb.(Phone);ok == true{
phone.Call()
}
usb.Stop()
}
func main() {
//定义一个Usb接口数组,可以存放Phone 和 Camera
var UsbArr [3]Usb
fmt.Print(UsbArr,"\n")
UsbArr[0] = Phone{"vivo",}
UsbArr[1] = Phone{"huawei",}
UsbArr[2] = Camera{"nikang"}
fmt.Print(UsbArr,"\n")
var computer Computer
for index, v :=range UsbArr{
fmt.Print(index)
computer.Working(v)
}
}
案例2: 写一个函数,循环判断传入参数的类型
func TypeJudge(items... interface{}) {
for index,x :=range items{
switch x.(type) {
case bool:
fmt.Printf("第%v个参数是bool,值是%v\n",index,x)
case float32:
fmt.Printf("第%v个参数是float32,值是%v\n",index,x)
case float64:
fmt.Printf("第%v个参数是float64,值是%v\n",index,x)
case int,int32,int64:
fmt.Printf("第%v个参数是整数,值是%v\n",index,x)
case string:
fmt.Printf("第%v个参数是string,值是%v\n",index,x)
default:
fmt.Printf("第%v个参数未找到\n",index)
}
}
}
func main() {
var n1 float32 = 1.1
var n2 int64 = 3
var n3 string = "hello"
TypeJudge(n1,n2,n3)
}