面向对象的三大特性

面向对象三大特性

前奏

分析银行卡

属性:账号,密码,余额

方法: 存款,取款,转账

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)

}

posted @ 2021-02-01 02:04  小子,你摊上事了  阅读(62)  评论(0编辑  收藏  举报