面向对象编程:
抽象的介绍:
我们在前面定义一个结构体时候,实际上就是把一类事物共有的属性(字段)和行为提取出来,形成一个物理模型(结构体),这种研究问题的方法就是抽象。
下面是一个银行模型
银行账号->属性(字段):
- 账号 2.密码 3.余额
(行为)方法:
1.存款 2.取款 3.查询
账号结构体:Account
代码实现:
package main
import "fmt"
type Account struct{
AccountNam string
Pwd string
TheLest int
}
func (ac *Account) SaveMoney(n int){
ac.TheLest += n
fmt.Println("存款成功...")
fmt.Printf("该账号名: %s\n您的余额为: %d",ac.AccountNam,ac.TheLest)
}
func (ac *Account) UseMoney(n int){
ac.TheLest -= n
fmt.Println("取款成功...")
fmt.Printf("该账号名: %s\n您的余额为: %d",ac.AccountNam,ac.TheLest)
}
func (ac *Account) LookTl(){
fmt.Printf("该账号名: %s\n您的余额为: %d",ac.AccountNam,ac.TheLest)
}
func main(){
var ac = Account{
AccountNam: "mary",
Pwd : "123123",
TheLest: 52369,
}
fmt.Printf("功能:\n1. 存款\n2.取款\n3.查询本账号信息\n")
var cmd int
fmt.Println("您需要...")
fmt.Scanf("%d",&cmd)
switch cmd{
case 1:
var money int
fmt.Println("请输入储存金额 :")
fmt.Scanf("%d",&cmd)
ac.SaveMoney(money)
case 2:
var money int
fmt.Println("请输入取款金额 :")
fmt.Scanf("%d",&cmd)
ac.UseMoney(money)
case 3:
ac.LookTl()
default :
fmt.Println("输入有错误....",cmd)
}
}
另外可以对方法进行系统优化,对操作者进行输入密码处理。
演示代码如下:
package main
import "fmt"
type Account struct{
AccountNo string
Pwd string
Balance float64
}
//方法:
//1. 存款:
func (account Account) Deposite(money float64,pwd string){
if pwd != account.Pwd{//有密码不等与account.Pwd,就输出密码错误
fmt.Println("您输入的密码有错误...")
return
}//代表次方法的结束
if money<=0 {
fmt.Println("您输入的金额错误...")
return
}
account.Balance += money
fmt.Println("存款成功....")
fmt.Printf("余额 : %f\n",account.Balance)
}
func (account Account) WithDraw(money float64,pwd string){
//看下密码是否正确
if pwd != account.Pwd{//有密码不等与account.Pwd,就输出密码错误
fmt.Println("您输入的密码有错误...")
return
}//代表次方法的结束
if money<=0 {
fmt.Println("您输入的金额错误...")
return
}
account.Balance -= money
fmt.Println("取款成功....")
fmt.Printf("余额 : %f\n",account.Balance)
}
func (account Account) Query(pwd string){
//看下密码是否正确
if pwd != account.Pwd{//有密码不等与account.Pwd,就输出密码错误
fmt.Println("您输入的密码有错误...")
return
}//代表次方法的结束
fmt.Println("您的信息为:")
fmt.Printf("AccountNumber : %s\nPwd : %s\nBalance :%f",
account.AccountNo,account.Pwd,account.Balance)
}
func main(){
//测试:
account := Account{
AccountNo: "小明",
Pwd : "6",
Balance: 856.99,
}
account.Query("6")
fmt.Println()
account.Deposite(200.0,"6")
fmt.Println()
account.Query("6")
fmt.Println()
account.WithDraw(150.0,"6")
fmt.Println()
account.Query("6")
}
其中还可以加上去一个操作菜单,再用switch语句进行命令和选项。
面向对象编程的三大特性-封装
封装基本介绍
封装就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作,才能对字段进行操作。
封装的理解和好处
- 隐藏细节
- 可以对数据进行验证,包证安全的可靠性
如何体现封装
- 对结构体中的属性(字段)进行封装(结构体内的字段小写字母,就是简单的封装)
- 通过方法、包实现封装
封装的实现步骤
- 将结构体、字段首字母小写(不可以导出,其他的包不能使用、类似其他的语言中的private)
- 给结构体所在包提供一个工厂模式的函数,首字母大写。(类似其他语言的public)
- 提供一个首字母大写的Set方法,用于对属性判断属性判断并复制
func (var 结构体类型名) SetXxx(参数列表){
return var.age
}
特别说明:在golang中开发中并没有特别强调封装,golagn本身对面向对象的特性进行了做了简化的
快速入门案例
看一个程序(persion.go),不能随便查看人的年龄、工资等隐私,并对输入的年龄进行合理的验证。设计model包(persion.go)main包(main.go 调用Persion结构体)
代码实现:
model/persion.go
persion.go:
package model
import "fmt"
type persion struct{
Name string
age int
salary float64//小写字母不能直接访问
}
//协议个工厂模式,相当与别的语言的构造函数
func NewPersion(name string) *persion{
//返回的是一个persion结构体的指针
return &persion{
Name : name ,
}
}//相当于SetName函数
func (p *persion) SetAge(age int){
if age >0&& age < 150{
p.age = age
}else {
fmt.Println("您输入的年龄有错误...")
//给以个默认值
}
}//这样就对输入年龄有进行合理的验证了
//另外还要设置一个Get函数,设置一对
func GetAge(p *persion) int{
return p.age
}
func (p *persion) SetSalary(money float64){
if money >=3000&&money<=30000{
p.salary =money
}else {
fmt.Println("您输入的金额有误....")
}
}
func GetSalary(p *persion) float64{
return p.salary
}
main.go:
package main
import(
"fmt"
"go_code/oop.ex/oop.ex5/model"
)
func main(){
p := model.NewPersion("jack")
fmt.Println(p)
p.SetAge(88888)
fmt.Println(p)
p.SetAge(78)
fmt.Println(p)
p.SetSalary(7892.6)
fmt.Println(p)
}
运行结果:
&{jack 0 0}
您输入的年龄有错误...
&{jack 0 0}
&{jack 78 0}
&{jack 78 7892.6}
练习
要求:1)创建程序,model包中定义一个Account结构体:在main函数中体系那golang的封装性
2)Account皆否提要求具有字段:账号:(长度在6~10之内)、余额(必须大于20)、密码(一定要是六位数)
3)通过SetXxx的方法来给Account的字段赋值。
4)在main函数中测试
model.go:
package model
import "fmt"
type Account struct {
account string
balance float64
pwd string
}
func (p *Account) Setaccount(name string){
if len(name)>=6&&len(name)<=10{
p.account = name
}else {
fmt.Println("输入账号名格式有错误...")
}
}
func (p *Account) Setbalance(money float64){
if money >= 20.0{
p.balance = money
fmt.Println("存款成功..,")
}else {
fmt.Println("存款金额有错误...")
}
}
func (p *Account) Setpwd(pwd string){
if len(pwd)!=6{
fmt.Println("密码格式有误,只能输入六位数...")
}else {
p.pwd = pwd
}
}
main.go:
package main
import (
"fmt"
"go_code/fengzhuang/model"
)
func main(){
persion := &model.Account{}
persion.Setaccount("刘杰")
persion.Setbalance(523.3)
persion.Setpwd("jinglu")
fmt.Println(*persion)
}
面向对象三大特性-继承
引出继承的必要性:
1)对于两个或者多个结构体的字段或者是方法几乎相同,但是我们却写了相同的代码,代码复用性不强
2)出现代码重复,而且代码不利于维护,同时也不利于功能的扩展。
3)解决方法-通过继承的凡是来解决
继承的基本介绍:
继承可以解决代码复用,让我们的编程更加接近人类思维
当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体这中定义这些相同的方法。
其他的结构体不需要重新定义这些字段和方法,只需要嵌套一个匿名结构体就可以了
嵌套结构体基本语法:
type Goods struct{
Name string
Price int
}
type Book struct{
Goods//在Book结构体中镶嵌了结构体Goods
Writrer string
}
快速入门:
编写一个学生考试系统,输出小学生,中学生,大学生还有研究生的考试状态
代码;
package main
import "fmt"
type Student struct{
Name string
Age int
Scores float64
}
type Pupil struct{
Student
}
type Bigpupil struct{
Student
}
func (stu *Student) State(){
fmt.Printf("姓名: %s\n年龄: %d\n成绩: %.1f\n",stu.Name,stu.Age,stu.Scores)
}
func (pupil *Pupil) testing(){
fmt.Printf("%s 在考试当中(小学生)....\n",pupil.Name)
}
func (bigstu *Bigpupil) testing(){
fmt.Printf("%s 在考试当中(大学生)....\n",bigstu.Name)
}
func main(){
pupil := &Pupil{Student{
Name: "jack",
Age: 15,
Scores: 98.9,
}}
Stu := &Bigpupil{Student{
Name: "mary",
Age: 28,
Scores: 483,
}}
pupil.testing()
pupil.State()
Stu.testing()
Stu.State()
}
终端输出:
jack 在考试当中(小学生)....
姓名: jack
年龄: 15
成绩: 98.9
mary 在考试当中(大学生)....
姓名: mary
年龄: 28
成绩: 483.0
继承带来的便利:
1)代码的复用性提高了
2)代码的扩展性和维护性提高了
继承的深入讨论:
1)结构体可以是用嵌套匿名结构体所有的字段和方法,即:首字母大小写的字段、方法都可以使用
2)匿名结构体字段可以简化
package main
import _"fmt"
type A struct{
name string
age int
scores float64
}
type B struct {
A
name string
}
//简化定义:
func main(){
var b B
b.name = "jack"//b中字段含有一个字段name
b.age = 18//简化定义,在b结构体中只有B中含有age
//相当于 b.A.age = 18
}
对上面代码的流程:
编译器会先看b对应的结构体中是否有name,如果有的话就直接定义的是对应的结构体内的字段中的name,假如没有的话就会在b对应的字段中的结构体中查找name,
如上面的b.age—b对应的结构体中没有age这一个字段,那么,编译器就会去b对应的结构体中的A结构体中查找age这一个字段,就这样一直找下去,找到最近的,假如没有就会报错。
接口:
基本介绍:
Golang中的多态是主要通过接口来实现的
快速入门
package main
import (
"fmt"
)
//声明(定义)一个接口
type Usb interface{
//声明两个没有实现的方法
Start()
Stop()
}
type Phone struct{
}
//手机实现了Usb的方法
func (phone Phone) Start(){
fmt.Println("手机开始工作····")
}
func (phone Phone) Stop(){
fmt.Println("手机停止工作····")
}
//Camera实现了接口Usb
type Camera struct{
}
func (camera Camera) Start(){
fmt.Println("相机开始工作····")
}
func (camera Camera) Stop(){
fmt.Println("相机停止工作····")
}
type Computer struct{
}
//编写一个Working方法,接收一个Usb接口类型变量
//只要实现了Usb接口(实现了接口的所有方法)
func (c Computer) Working(usb Usb){
usb.Start()
usb.Stop()
}
func main(){
//测试
computer := Computer{}
phone := Phone{}
camera := Camera{}
computer.Working(phone)
computer.Working(camera)
computer.Working(phone)
}
小结说明:
- 接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法,接口体现了程序设计的多态和高内聚低耦合的思想
- Golang中的接口,不需要显示的实现,只要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口,因此,Golang中没有implement这样的关键字