封装
案例:
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.Println("年龄范围不正确。。。")
}
}
func (p *person) GetAge() int {
return p.age
}
func (p *person) SetSal(sal float64) {
if sal > 3000 && sal < 30000 {
p.sal = sal
} else {
fmt.Println("薪水范围不正确。。。")
}
}
func (p *person) GetSal() float64 {
return p.sal
}
package main
import (
"fmt"
"go_code/demo02/model"
)
func main() {
p := model.NewPerson("smith")
p.SetAge(18)
p.SetSal(10000)
fmt.Println(p)
}
练习
创建程序,在model包中定义Account结构体,在main函数中体会Golang的封装性。
Account结构体要求具有字段:账号(长度在6-10之间)、余额(必须>20)、密码(b必须是六位)
通过SetXXX的方法给Account的字段赋值
在main函数中完成测试
package model
import "fmt"
type account struct {
accountNo string
pwd string
balance float64
}
// 工厂模式函数
func NewAccount(accountNo string, pwd string, blannce float64) *account {
if len(accountNo) < 6 || len(accountNo) > 10 {
fmt.Println("账号的长度不对...")
return nil
}
if len(pwd) != 6 {
fmt.Println("密码的长度不对...")
return nil
}
if blannce < 20 {
fmt.Println("余额的数目不对...")
return nil
}
return &account{
accountNo: accountNo,
pwd: pwd,
balance: blannce,
}
}
func (account *account) SetAccountNo(accountNo string) {
account.accountNo = accountNo
}
func (account *account) GetAccountNo() string {
return account.accountNo
}
func (account *account) SetPwd(pwd string) {
account.pwd = pwd
}
func (account *account) GetPwd() string {
return account.pwd
}
func (account *account) SetBalance(balance float64) {
account.balance = balance
}
func (account *account) GetBalance() float64 {
return account.balance
}
func (account *account) Deposite(money float64, pwd string) {
if pwd != account.pwd {
fmt.Println("你输入的密码不正确")
return
}
if money <= 0 || money > account.balance {
fmt.Println("你输入的金额不正确")
return
}
account.balance -= money
fmt.Println("取款成功")
}
func (account *account) Query(pwd string) {
if pwd != account.pwd {
fmt.Println("你输入的密码不正确")
return
}
}
package main
import (
"fmt"
"go_code/demo03/model"
)
func main() {
account := model.NewAccount("noyeyh", "123456", 10000)
if account != nil {
fmt.Println("创建成功=", account)
} else {
fmt.Println("创建失败")
}
}
继承
package main
import "fmt"
type Pupil struct {
Name string
Age int
Score int
}
func (p *Pupil) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 分数=%v\n", p.Name, p.Age, p.Score)
}
func (p *Pupil) SetSore(score int) {
p.Score = score
}
func (p *Pupil) testing() {
fmt.Println("小学生正在考试中")
}
type Graduate struct {
Name string
Age int
Score int
}
func (p *Graduate) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 分数=%v\n", p.Name, p.Age, p.Score)
}
func (p *Graduate) SetSore(score int) {
p.Score = score
}
func (p *Graduate) testing() {
fmt.Println("大学生正在考试中")
}
func main() {
var graduate = &Graduate{
Name: "tom",
Age: 10,
}
graduate.testing()
graduate.SetSore(100)
graduate.ShowInfo()
}
pupil和graduate 两个结构体的字段和方法几乎相同,写了相同的代码
出现代码冗余,不利于代码维护,也不利于功能扩展
介绍
继承可以解决代码复用的问题,让编程更加靠近人类思维
当多个结构体存在相同的属性和方法时,可以从这些结构体中抽象出结构体,在结构体中定义相同的属性和方法
其他的结构体不需要重新定义这些属性和方法,只需要嵌套一个Student匿名结构体即可。
嵌套匿名结构体的基本语法
type Goods struct{
Name string
Price int
}
type Book struct{
Goods
Writer string
}
案例
package main
import "fmt"
type Student struct {
Name string
Age int
Score int
}
func (p *Student) ShowInfo() {
fmt.Printf("学生名=%v 年龄=%v 分数=%v\n", p.Name, p.Age, p.Score)
}
func (p *Student) SetSore(score int) {
p.Score = score
}
type Pupil struct {
Student
}
func (p *Pupil) testing() {
fmt.Println("小学生正在考试中")
}
type Graduate struct {
Student
}
func (p *Graduate) testing() {
fmt.Println("大学生正在考试中")
}
func main() {
// 嵌入结构体后
pupil := &Pupil{}
pupil.Student.Name = "tom~"
pupil.Student.Age = 8
pupil.testing()
pupil.Student.SetSore(70)
pupil.Student.ShowInfo()
// 嵌入结构体后
graduate := &Graduate{}
graduate.Student.Name = "Mary~"
graduate.Student.Age = 28
graduate.testing()
graduate.Student.SetSore(90)
graduate.Student.ShowInfo()
}
代码的复用性提高了
代码的扩展性和维护性提高了
深入讨论
结构体可以使用嵌套匿名结构体所有字段和方法,即:首字母大写或者小写的字段、方法,都可以使用
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 = 18
b.A.sayOK()
b.A.hello()
}
匿名结构体字段访问可以简化
b.Name = "Tom" // b.A.Name = "Tom"
b.age = 18 // b.A.age = 18
b.sayOK() // b.A.sayOK()
b.hello() // b.A.hello()
当我们直接通过b访问字段或方法时,其执行流程如上
编译器会先看b对应的类型有没有Name,如果有,则直接调用B类型的Name字段
如果没有就去看B中嵌入的匿名结构体A有没有声明Name字段,如果没有继续查找,如果都没找到会直接报错
当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体来区分
结构体嵌入两个匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错
如果一个struct嵌套了一个又名结构体,这种模式就是组合,如果是组合关系,那么在访问组合结构体的字段或方式是,必须带上结构体的名字
嵌套匿名结构体后,也可以在创建结构体变量时,直接指定各个匿名结构体字段的值
接口(interface)
package main
import "fmt"
type Usb interface {
Start()
Stop()
}
type Phone struct {
}
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 {
}
func (c Computer) Woking(usb Usb) {
usb.Start()
usb.Stop()
}
func main() {
computer := Computer{}
phone := Phone{}
camera := Camera{}
computer.Woking(phone)
computer.Woking(camera)
}
介绍
interface类型可以定义一组方法,但都不需要实现,并且interface不能包含任何变量,到某个自定义类型,要使用的时候,再根据具体情况把这些方法写出来
基本语法
type 接口名 interface{
method1的(参数列表) 返回值列表
method2的(参数列表) 返回值列表
}
接口的所有方法都没有方法体,即接口的方法都是没有实现的方法,接口体现了程序设计的多态和高内聚低耦合的思想
Golang中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就是先这个接口。Golang中没有implement关键字
注意事项
接口本身不能创建实例,但可以指向一个实现了该接口的自定义类型的实例
接口中所有的方法都没有方法体,即都是没有实现的方法
在Golang中,一个自定义类型需要将某个接口的所有方法都实现,我们舒总合格自定义类型实现了该接口
一个自定义类型只有实现了某个接口,再能将该自定义类型的实例,付给接口类型
只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
一个自定义类型可以实现多个接口
Golang接口中不能有任何变量
一个接口(比如A接口)可以继承多个别的接口(比如B,C接口),这时如果要实现A接口,也必须将B,C接口的方法也全部实现
interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil
空接口interface{}没有任何方法,所以所有类型都实现了空接口
接口的实践
实现对Hero结构体切片的排序:sort.Sort(data Interface)
package main
import (
"fmt"
"math/rand"
"sort"
)
type Hero struct {
Name string
Age int
}
type HeroSlice []Hero
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].Age < hs[j].Age
}
func (hs HeroSlice) Swap(i, j int) {
temp := hs[i]
hs[i] = hs[j]
hs[j] = temp
}
func main() {
var intSlice = []int{0, -1, 10, 7, 90}
// 系统提供的排序
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),
}
heroes = append(heroes, hero)
}
for _, v := range heroes {
fmt.Println(v)
}
sort.Sort(heroes)
fmt.Println("排序后---------------------")
for _, v := range heroes {
fmt.Println(v)
}
}
多态
介绍
多态的特征是通过接口实现的,可以按照统一的接口来调用不同的实现,这是接口变量就是呈现不同的形态
接口体现多态的特征
多态参数
在前面的Usb接口案例,Usb usb,即可以接收手机变量,又可以接收相机变量,就体现了接口的多态
多态数组
package main
import "fmt"
type Usb interface {
Start()
Stop()
}
type Phone struct {
Name string
}
func (p Phone) Start() {
fmt.Println("手机开始工作...")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作...")
}
type Camera struct {
Name string
}
func (c Camera) Start() {
fmt.Println("相机开始工作...")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作...")
}
type Computer struct {
}
func (c Computer) Woking(usb Usb) {
usb.Start()
usb.Stop()
}
func main() {
// 定义一个Usb几口数组,可以存放Phone和Camear的结构体变量
// 多态数组
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"mini"}
usbArr[2] = Camera{"尼康"}
fmt.Println(usbArr)
}
类型断言
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 不可以
b = a.(Point)
fmt.Println(b)
}
基本介绍
类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类习惯,就需要使用类型断言
var x interface{}
var b2 float32 = 1.1
x = b2
y := x.(float32)
fmt.Printf("y 的类型是%T 值是=%v", y, y)
在进行类型断言时,如果类型不匹配,就会报panic,因此进行类型断言时,要确保原来的空接口指向的就是断言类型
如何在进行断言时,单上检测机制,如果成功就ok,否则也不要报panic
y, ok := x.(float32)
if ok {
fmt.Println("success")
fmt.Printf("y 的类型是%T 值是=%v", y, y)
} else {
fmt.Println("convert fail")
}
fmt.Println("继续执行...")
项目开发流程
需求分析
30%
需求分析师:
懂技术
懂业务
结果:需求分析报告
设计阶段
20%
项目经理
架构 [开发语言、框架、数据库、操作系统。。。]
选人[]
结果:设计文档、类图。。数据库,界面原型
代码实现
20%
软件工程师[码农]
实现各个模块
测试阶段
软件测试工程师(用友)
黑盒测试
白盒测试(懂代码)
灰盒测试
实施阶段
实施工程师
维护阶段
用户发现问题
文件操作
文件是数据源的一种,最主要的作用就是保存数据,即可以保存图片,也可以保存视频。。。
基本介绍
文件在程序中是以流的形式来操作的。
流:数据在数据源和程序之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
常用的文件操作函数和方法
打开一个文件进行读操作
os.Open(name string)(*File,error)
关闭一个文件
File.Close
package main
import (
"fmt"
"os"
)
func main() {
// file叫file对象 (file指针,file句柄)
file, err := os.Open("F:/goproject/src/go_file/test.txt")
if err != nil {
fmt.Println("open file err=", err)
}
fmt.Printf("file=%v", file)
err = file.Close()
if err != nil {
fmt.Println("close file err=", err)
}
}
读文件操作应用实例
读取文件的内容并显示在终端(带缓冲的方式),使用os.Open,file.Colse,bufio.NewReader(),reader.ReadString函数和方法
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// file叫file对象 (file指针,file句柄)
file, err := os.Open("F:/goproject/src/go_file/test.txt")
if err != nil {
fmt.Println("open file err=", err)
}
fmt.Printf("file=%v", file)
err = file.Close()
if err != nil {
fmt.Println("close file err=", err)
}
// 但函数退出时,要及时关闭file
defer file.Close() //要及时关闭file句柄,否则会有内存泄漏
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n')
if err == io.EOF {
break
}
fmt.Print(str)
}
fmt.Println("文件读取结束...")
}