Golang学习 Day_11

封装

案例:

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的封装性。

  1. Account结构体要求具有字段:账号(长度在6-10之间)、余额(必须>20)、密码(b必须是六位)

  1. 通过SetXXX的方法给Account的字段赋值

  1. 在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()
}
  1. pupil和graduate 两个结构体的字段和方法几乎相同,写了相同的代码

  1. 出现代码冗余,不利于代码维护,也不利于功能扩展

介绍

继承可以解决代码复用的问题,让编程更加靠近人类思维

当多个结构体存在相同的属性和方法时,可以从这些结构体中抽象出结构体,在结构体中定义相同的属性和方法

其他的结构体不需要重新定义这些属性和方法,只需要嵌套一个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()
}
  1. 代码的复用性提高了

  1. 代码的扩展性和维护性提高了

深入讨论

  1. 结构体可以使用嵌套匿名结构体所有字段和方法,即:首字母大写或者小写的字段、方法,都可以使用

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()


}
  1. 匿名结构体字段访问可以简化

    b.Name = "Tom" // b.A.Name = "Tom"
    b.age = 18     // b.A.age = 18
    b.sayOK()      // b.A.sayOK()
    b.hello()      // b.A.hello()
  1. 当我们直接通过b访问字段或方法时,其执行流程如上

  1. 编译器会先看b对应的类型有没有Name,如果有,则直接调用B类型的Name字段

  1. 如果没有就去看B中嵌入的匿名结构体A有没有声明Name字段,如果没有继续查找,如果都没找到会直接报错

  1. 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体来区分

  1. 结构体嵌入两个匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错

  1. 如果一个struct嵌套了一个又名结构体,这种模式就是组合,如果是组合关系,那么在访问组合结构体的字段或方式是,必须带上结构体的名字

  1. 嵌套匿名结构体后,也可以在创建结构体变量时,直接指定各个匿名结构体字段的值

接口(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的(参数列表) 返回值列表

}

  1. 接口的所有方法都没有方法体,即接口的方法都是没有实现的方法,接口体现了程序设计的多态和高内聚低耦合的思想

  1. Golang中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就是先这个接口。Golang中没有implement关键字

注意事项

  1. 接口本身不能创建实例,但可以指向一个实现了该接口的自定义类型的实例

  1. 接口中所有的方法都没有方法体,即都是没有实现的方法

  1. 在Golang中,一个自定义类型需要将某个接口的所有方法都实现,我们舒总合格自定义类型实现了该接口

  1. 一个自定义类型只有实现了某个接口,再能将该自定义类型的实例,付给接口类型

  1. 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型

  1. 一个自定义类型可以实现多个接口

  1. Golang接口中不能有任何变量

  1. 一个接口(比如A接口)可以继承多个别的接口(比如B,C接口),这时如果要实现A接口,也必须将B,C接口的方法也全部实现

  1. interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil

  1. 空接口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)
    }
}

多态

介绍

多态的特征是通过接口实现的,可以按照统一的接口来调用不同的实现,这是接口变量就是呈现不同的形态

接口体现多态的特征

  1. 多态参数

在前面的Usb接口案例,Usb usb,即可以接收手机变量,又可以接收相机变量,就体现了接口的多态

  1. 多态数组

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("继续执行...")

项目开发流程

  1. 需求分析

  1. 30%

  1. 需求分析师:

  1. 懂技术

  1. 懂业务

  1. 结果:需求分析报告

  1. 设计阶段

  1. 20%

  1. 项目经理

  1. 架构 [开发语言、框架、数据库、操作系统。。。]

  1. 选人[]

  1. 结果:设计文档、类图。。数据库,界面原型

  1. 代码实现

  1. 20%

  1. 软件工程师[码农]

  1. 实现各个模块

  1. 测试阶段

  1. 软件测试工程师(用友)

  1. 黑盒测试

  1. 白盒测试(懂代码)

  1. 灰盒测试

  1. 实施阶段

  1. 实施工程师

  1. 维护阶段

  1. 用户发现问题

文件操作

文件是数据源的一种,最主要的作用就是保存数据,即可以保存图片,也可以保存视频。。。

基本介绍

文件在程序中是以流的形式来操作的。

流:数据在数据源和程序之间经历的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源(文件)的路径

常用的文件操作函数和方法

  1. 打开一个文件进行读操作

os.Open(name string)(*File,error)

  1. 关闭一个文件

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)
    }
}

读文件操作应用实例

  1. 读取文件的内容并显示在终端(带缓冲的方式),使用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("文件读取结束...")
}