面向对象的三大特性
面向对象三大特性
前奏
分析银行卡
属性:账号,密码,余额
方法: 存款,取款,转账
package main
import "fmt"
//创建一个结构体
type Account struct {
AccountNo string
Pwd string
Balance float64
}
// 存款
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("存款成功")
}
//取款
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.Printf("您的账号为=%v,余额为=%v 取款成功 \n", account.AccountNo, account.Balance)
}
//查询
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{"ny1093943","000000",0}
account.Deposite(10000,"000000")
account.Query("000000")
account.WithDraw(100,"000000")
}
封装
- 将结构体,字段的首字母小写
- 给结构体所在的包提供一个工厂模式,首字母大写,类似于构造一个函数
案例
写一个程序,不能随便查看别人的年龄,工资登隐私,并对输出的年龄进行合理验证
# main
package main
import (
"fmt"
"love/s15day/model"
)
func main() {
p := model.NewPerson("zc")
p.SetAge(17)
p.SetsSal(10000)
fmt.Println(*p,p.GetAge(),p.GetsSal())
}
# model
package model
import "fmt"
//构造一个
type person struct {
Name string
age int
sal float64
}
//使用一个工厂模式,相当于构造一个函数
func NewPerson(name string) *person {
return &person{
Name: name,
}
}
//为了访问ahe和val,我们编写一个Set方法
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) SetsSal(sal float64) {
if sal >= 3000 && sal <= 30000 {
p.sal = sal
} else {
fmt.Println("输入的范围不正确")
}
}
func (p *person) GetsSal() float64 {
return p.sal
}
创建程序,在model包张定义一个Account结构体,
account字段:账号(长度在6-10),余额(>20),密码(六位)通过Setxxx的方法给Account的字段赋值
# main.go
package main
import (
"fmt"
"love/s15day/model"
)
func main() {
account := model.Newaccount("111111","111111",2000)
if account != nil {
fmt.Println("创建成功=",*account)
} else {
fmt.Println("创建失败")
}
}
# model.go
package model
import "fmt"
//创建一个结构体
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 (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("存款成功")
}
//取款
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.Printf("您的账号为=%v,余额为=%v 取款成功 \n", account.accountNo, account.balance)
}
//查询
func (account *account) Query(pwd string) {
//看一下输入的密码是否正确
if pwd != account.pwd {
fmt.Println("您输入的密码不正确")
return
}
//看取款金额是否正确
fmt.Printf("您的账号为=%v,余额为=%v \n", account.accountNo, account.balance)
}
继承
只需要在嵌套一个匿名结构体即可
基本语法
type Goods struct {
Name string
Price int
}
type Book struct {
Goods // 这里就是嵌套匿名结构体Goods
Writer string
}
案例
package main
import "fmt"
//编写一个学生考试系统
type Student struct {
Name string
Age int
Score float64
}
//显示他的成绩
func (stu *Student) ShowInfo() {
fmt.Printf("学生名: %v, 年龄: %v, 成绩:%v \n", stu.Name, stu.Age, stu.Score)
}
func (stu *Student) SetScore(score float64) {
stu.Score = score
}
//小学生
type Pulic struct {
Student
}
//大学生
type Gradute struct {
Student
}
func (p *Pulic) Testing() {
fmt.Println("小学生正在考试...")
}
func (p *Gradute) Testing() {
fmt.Println("大学生正在考试...")
}
func main() {
//pulic := &Pulic{}
//pulic.Student.Name = "tom"
//pulic.Student.Age = 10
//pulic.Testing()
//pulic.Student.SetScore(70)
//pulic.Student.ShowInfo()
pubic := Pulic{
Student{"tom", 10, 90},
}
fmt.Println(pubic)
}
细节
-
结构体可以使用嵌套匿名结构体所有的字段和方法,首字母大小写都可以使用
package main import "fmt" type A struct { Name string age int } func (a *A) SayOk() { fmt.Println("A SayOk", a.Name) } func (a *A) hello() { fmt.Println("A hello", a.Name) } type B struct { A } func main() { var b B b.A.Name = "TOM" b.A.age = 10 b.A.SayOk() b.A.hello() //也可以把A去掉 }
-
当结构体和匿名结构体有相同的字段和方法时,采用就近原则访问,如果希望访问匿名结构体的方法和字段,可以通过匿名结构体名来区别
package main import "fmt" type A struct { Name string age int } func (a *A) SayOk() { fmt.Println("A SayOk", a.Name) } func (a *A) hello() { fmt.Println("A hello", a.Name) } type B struct { A Name string } func (b *B) SayOk() { fmt.Println("B SayOk", b.Name) } func main() { var b B b.Name ="jack" b.age = 100 b.SayOk() b.A.SayOk() //指明结构体即可 b.hello() }
-
结构体嵌入多个匿名结构体,如果两个匿名结构体有相同的字段和方法,在访问时,必须知名匿名结构体名字
package main import "fmt" type A struct { Name string age int } type B struct { Name string Score float64 } type C struct { A B //Name string } func main() { var c C //如果c没有Name字段,A和B有,必须指明匿名结构体 //c.Name ="tom" c.A.Name = "TOM" fmt.Println("c") }
-
如果一个struct嵌套一个又名结构体,这种模式叫组合,如果时组合的关系,那么在访问组合结构体的字段和方法时,必须带上结构体的名字
package main type A struct { Name string age int } type B struct { Name string Score float64 } type C struct { A B //Name string } type D struct { a A //有名结构体 } func main() { var d D d.a.Name = "TOM" }
-
嵌套匿名结构体后,也可以在创建结构体变量,直接指定各个匿名结构体字段的值
package main import "fmt" type A struct { Name string age int } type B struct { Name string Score float64 } type C struct { A B //Name string } type D struct { a A //有名结构体 } 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{"电视001", 100}, Brand{"海尔001", "大行山"}} tv2 := TV{ Goods{Price: 1000, Name: "电视002"}, Brand{"海尔002", "日本"}, } fmt.Println(tv, tv2) tv3 := TV2{&Goods{"电视001", 100}, &Brand{"海尔001", "大行山"}} fmt.Println(*tv3.Goods,*tv3.Brand) }
接口
使用
interface 类型可以定义一组方法,但是这些不需要实现,并且interface不能包含任何变量,到某个自定义类型要使用的时候,在根据具体情况把这些方法写出来
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* 定义结构体 */
type struct_name struct {
/* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* 方法实现*/
}
- 接口里面所有的方法都没影方法体,接口体现了程序设计的堕胎和高内聚低耦合的思想
- go接口,不需要显示的实现,只要一个变量,含有接口类型中所有方法,那么这个变量就实现这个接口
接口注意事项
-
接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量
package main import "fmt" type AInterface interface { Say() } type Stu struct { Name string } func (stu Stu) Say() { fmt.Println("Stu Say()") } func main() { var stu Stu stu.Say() }
-
接口中所有的方法都没方法体,都是没有实现的方法
-
一个自定义类型需要将某个接口所有的方法都是先,我们说这个自定义类型实现了该接口
-
一个自定义类型只有实现了某个接口,才能将该自定义类型的实例赋给接口类型
-
只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
package main import "fmt" type AInterface interface { Say() } type Stu struct { Name string } func (stu Stu) Say() { fmt.Println("Stu Say()") } type integer int func (i integer) Say(){ fmt.Println("integer Say i=",i) } func main() { var stu Stu stu.Say() var i integer = 10 var b AInterface = i b.Say() }
-
一个自定义类型可以实现多个接口
package main import "fmt" type AInterface interface { Say() } type Binterface interface { Hello() } type Monster struct { } func (m Monster) Hello(){ fmt.Println("Monster Hello()") } func (m Monster) Say(){ fmt.Println("Monster Say()") } func main() { //Monster 实现了AInterface 和 BInterface 接口 var monster Monster var a2 AInterface = monster var b2 Binterface = monster a2.Say() b2.Hello() }
-
接口中不能有任何变量
-
一个接口可以继承多个别的接口,这是如果要实现A接口,也必须将B接口的方法也全部实现
package main type BInterface interface { test01() } type CInterface interface { test02() } type AInterface interface { BInterface CInterface test03() } //如果需要实现AInterface 就需要将BInterface和CInterface的方法都实现 type Stu struct { } func (stu Stu) test01(){ } func (stu Stu) test02(){ } func (stu Stu) test03(){ } func main() { var stu Stu var a AInterface=stu a.test01() }
-
interface 类型默认是一个指针,如果没有interface初始化就是要,那就会输出nil
-
空接口看interface{}没有任何方法,所有类型都实现了空接口
接口实践
实现对Hero结构体切片的排序:sort.Sort()
package main
import (
"fmt"
"math/rand"
"sort"
)
//1. 声明hero结构体
type Hero struct {
Name string
Age int
}
//2. 声明一个hero结构体切片类型
type HeroSlice []Hero
//3. 实现Interface 接口
func (hs HeroSlice) Len() int {
return len(hs)
}
//Less 方法就是决定你使用声明标准进行排序
//按年龄排序
func (hs HeroSlice) Less(i, j int) bool {
//return hs[i].Age < hs[j].Age
return hs[i].Name < hs[j].Name
}
//swap 交换
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() {
// 实现对hero结构体切片的排序
//先定义一个数组/切片
intSlice := []int{0, -1, 10, 29, 7, 19}
//对切片进行排序
sort.Ints(intSlice)
fmt.Println(intSlice)
//对结构体切片进行排序
var heroes HeroSlice
for i := 0; i < 10; i++ {
hero := Hero{
Name: fmt.Sprintf("英雄~ %d", rand.Intn(100)),
Age: rand.Intn(100)}
//将hero append 到heros切片
heroes = append(heroes, hero)
}
//看看排序前的顺序
for _, v := range heroes {
fmt.Println(v)
}
fmt.Println("排序前___________")
//调用sort.Sort
sort.Sort(heroes)
for _, v := range heroes {
fmt.Println(v)
}
}
继承和接口的区别
-
当A结构体继承了B结构体,那么A就自动继承了B所有的字段和方法,可以直接使用
-
当A结构体需要扩展功能,同时不希望破环继承的关系,可以使用接口来实现,接口就是对继承的补充
package main import "fmt" //假如现在小猴子会爬树,但是还想学游泳,但是他继承猴子所以不能再定义一个方法,但是可以使用接口来完成功能的扩展 //接口就是继承功能的扩展 type Monkey struct { Name string } type LittleMonkey struct { Monkey // 继承 } //声明接口 type BirdAble interface { Flying() } //让littleMonkey来实现BirdAble func (this *LittleMonkey) Flying() { fmt.Println(this.Name, "通过学习,会飞了..") } func (this *Monkey) climbing() { fmt.Println(this.Name, "生来会爬树 ..") } func main() { //创建一个LittleMonkey实例 monkey := LittleMonkey{ Monkey{Name: "悟空"}, } monkey.climbing() monkey.Flying() }
多态
接口体现多态的两种形式
-
多态参数:就是前面的usb接口
-
多态数组:在Usb数组中,存放Phone结构体和Camera结构体变量
package main import "fmt" //声明定义一个接口 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 { } func (c Camera) Start() { fmt.Println("相机开始工作了") } func (c Camera) Stop() { fmt.Println("相机停止工作了") } type Computer struct { } //编写一个方法working,接收usb接口类型变量 //只要实现了usb接口,就是指实现了usb接口声明所有的方法 func (c Computer) Working(usb Usb) { //通过usb接口变量来调用Start,Stop方法 usb.Start() usb.Stop() } func main() { //定义一个Usb接口数组, 可以存放Phone和Camear的结构体变量 usArr :=[...]Usb{Phone{},Phone{},Camera{}} fmt.Println(usArr) }
在Usb数组中,存放Phone结构体和Camera结构体变量,Phone还有一个特有的方法call(),在遍历数组,如果Phone变量,除了调用Usb接口声明的方法外,还需要调用Phone,特有的方法,可以使用类型断言
如果你在接口定义一个call的方法,但是他是不能确定是手机的里面的还是相机里面的
类型断言
package main
import "fmt"
type Point struct {
x int
y int
}
func main() {
var a interface{}//空接口,可以接收任意类型
var point Point = Point{1, 2}
a = point
var b Point
b = a.(Point)
fmt.Println(b)
var x interface{}
var b2 float32 =1.1
x = b2
if y,ok := x.(float32); ok == true{
fmt.Println("转换成功=",y)
}else {
fmt.Println("转换失败")
}
}
案例改进
给Phone结构体增加一个特有的方法call(),Usb接口接收的是Phone变时,还需要调用call()方法
package main
import "fmt"
//声明定义一个接口
type Usb interface {
Start()
Stop()
}
type Phone struct {
name string
}
//让phone实现Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作了")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作了")
}
func (p Phone) Call() {
fmt.Println("手机正在打电话..")
}
type Camera struct {
name string
}
func (c Camera) Start() {
fmt.Println("相机开始工作了")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作了")
}
type Computer struct {
}
//编写一个方法working,接收usb接口类型变量
//只要实现了usb接口,就是指实现了usb接口声明所有的方法
func (c Computer) Working(usb Usb) {
//通过usb接口变量来调用Start,Stop方法
usb.Start()
//如果usb指向phone结构体变量,还需要调用call方法,可以使用类型断言
if phone, ok :=usb.(Phone);ok{
phone.Call()
}
usb.Stop()
}
func main() {
//定义一个Usb接口数组, 可以存放Phone和Camear的结构体变量
usArr := [...]Usb{Phone{"vivo"}, Phone{"huawei"}, Camera{"尼康"}}
var commputer Computer
for _, v := range usArr{
commputer.Working(v)
}
}
循环判断传入参数的类型
package main
import "fmt"
type Student struct {
}
//编写一个函数,可以判断输入的参数是声明类型
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)
case Student:
fmt.Printf("第%v个参数时 Student 类型,值是%v \n", index, x)
case *Student:
fmt.Printf("第%v个参数时 *Student 类型,值是%v \n", index, x)
default:
fmt.Printf("第%v个参数时 类型不确定,值是%v \n", index, x)
}
}
}
func main() {
var n1 float32 = 1.1
var n2 float64 = 1.9
var n3 int32 = 30
var name string = "tom"
n4:=300
stu1 := Student{}
stu2 := &Student{}
TypeJudge(n1,n2,n3,name,n4,stu1,stu2)
}