我们前面有了解到,在一个包中定义的对象名称必须是大写才能在其他的包中进行使用

但在不同的包调用时会出现一个问题,我在两个包中定义了相同名称的对象,那么我们就必须要用小写来避免重复

所以我们需要将对象的名称小写,然后通过工厂模式来调用不同包中小写开头的对象

 创建新目录model

mkdir model
touch model/mode.go

 在mode.go中添加一个结构体

案例1  结构体对象小写调用方法

先定义一个正常的结构体包调用

package model

type Student struct{    //这里定义时首字母先大写,允许外部的包调用
  Name string
  Score float64
}

 main代码

package main

import (
	"fmt"
	"test/model"     //导入包名称  起个别名
)

func main(){
	var stu = model.Student{    //包名称.结构体
		Name: "tom",
		Score : 78.9,
	}
	fmt.Println(stu)
}

返回

{tom 78.9}

这样是可以正常使用的,而当我们将type student struct 定义为小写时,将无法调用,下面使用工厂模式

mode.go

package model


type student struct{  //因为student结构体首字母是小写,因此是只能在model包使用
	Name string       //"结构体中的字段" 必须也要大写开头,不然无法在其他包里调用
	Score float64
}


//工厂模式,相当于暴露给其他包一个提供访问的路口,这里我们定义NewStudent的函数来调用结构体
//只需要在调用结构体之前将参数交给该入口函数即可调用未暴露的结构体
func NewStudent(n string,s float64) *student {
	return &student{
		Name: n,
		Score: s,
	}
}

 main.go

package main

import (
	"fmt"
	"test/model"     //导入包名称  起个别名
)

func main(){

	var stu = model.NewStudent("tom~",88.8)  //我们通过调用model包下的NewStudent来使用结构体

	fmt.Println(*stu)
}

案例2 结构体字段小写的解决方法

mode.go

package model


type Student struct{
	Name string
	score float64    //结构体字段小写,则外部无法调用
}


//如果score字段首字母小写,则在其他包不可以直接方法,我们可以提供一个方法
//我们可以访问一个对外公开的代码来访问 不对外公开的代码
func (s *Student) GetScore(n float64) float64{
	s.score = n
	return s.score     //本方法是可以访问本包的结构体字段的
}

main.go

package main

import (
	"fmt"
	"test/model"     //导入包名称  起个别名
)

func main(){
	var stu = model.Student{}
	stu.GetScore(7.7)
	fmt.Println(stu)

}

案例3 当结构体名称、字段都是小写

main.go

package main

import (
	"fmt"
	"test/model"     //导入包名称  起个别名
)

func main(){
    var stu = model.NewStudent("张三",7.1)  //先声明结构体变量
    fmt.Println(*stu)                            //输出
	fmt.Println("name=", stu.Name, " score=", stu.GetScore(7.7))  
      //单独调用,Name首字母大写可以直接调用
	 //score是小写,需要调用方法使用,这里会替换原先的值

}

mode.go

package model


type student struct{
	Name string
	score float64
}

func (s *student) GetScore(n float64) float64{
	s.score = n
	return s.score    
}

func NewStudent(n string,s float64) *student {
	return &student{
		Name: n,
		score: s,
	}
}

二、封装

把抽象出来的字段和对字段的操作封装在一起,数据在被保护在内部

程序的其他包通过对外暴露的方法来进行访问

封装实现步骤

1、 将结构体、字段(属性)的首字母小写 //小写后,其他包就不能使用了

2、 将结构体所在的包提供一个工厂模式的函数, 首字母大写

3、 提供一个首字母大写的Set方法,用于对属性判断并赋值
    func (var 结构体类型名) Sexxxx(参数列表)(返回值列表){
        //加入数据验证的业务逻辑
        var 字段 = 参数
    }

4、 提供一个首字母大写的Get方法,用于获取属性的值
    func (var 结构体类型名)  GetXX(){
         return var 字段
    }

案例

不能随便查看人的年龄,工资等隐私,并对输入的年龄进行合理的验证,

main.go

package main

import (
	"fmt"
	"test/model"
)

func main() {

	p := model.NewPerson("史密斯")   //通过工厂调用工厂函数,传入一个用户的名称

	p.SetAge(18)        //通过工厂函数修改 用户的年龄和收入
	p.Setsal(5000)

	fmt.Println(*p)          //查看信息
	fmt.Println(p.Name)
	fmt.Println(p.GetAge())    //age 和sal 是小写,需要通过工厂方法查看
	fmt.Println(p.Getsal())    //调用工厂方法,需要先通过工厂函数声明对应的一个结构体变量才可以使用
}

 mode.go

package model

import "fmt"

type person struct{
	Name string    //名称公开
	age int        //其他包无法访问小写字段
	sal float64

}


func NewPerson(name string) *person{  //定义一个工厂函数,用于外部调用结构体
	return &person{
		Name : name,
	}
}


func (p *person) SetAge(age int) {     //定义1  定义年龄
	if age > 0 && age <150 {           //允许输入一个年龄,判断是否正常,如果正常则写入年龄
		p.age = age
	}else{
		fmt.Println("年龄范围不正确")
	}
}

func (p *person) GetAge() int{       //方法2  查看年龄
	return p.age
}




func (p *person) Setsal(sal float64) {     //方法1  修改薪水
	if sal > 3000 && sal <= 30000 {
		p.sal = sal
	}else{
		fmt.Println("年龄范围不正确")
	}

}

func (p *person) Getsal() float64{         //方法2  查看薪水
	return p.sal
}

运行main

{史密斯 18 5000}
史密斯
18
5000

小练习

//创建程序,在model包中定义account结构体,在main函数中体会golang的封装特性
1、 account结构体要求具有字段:
    账号(长度在6-10之间)
    余额(必须> 20 )
    密码(必须是6位)
2、通过Setxxx的方法给Account的字段赋值

3、在main函数中测试

mode.go

package model

import "fmt"

type account struct{
	username string    //名称公开
	password string        //其他包无法访问小写字段
	sal float64

}


func NewPerson(username string,password string,sal float64) *account{  //定义一个工厂函数,用于外部调用结构体
	return &account{
		username : username,
		password : password,
		sal: sal,
	}
}


func (p *account) SetUsername(username string) {
	if len(username)  >= 6 && len(username) <= 10 {
		p.username = username
	}else{
		fmt.Println("账户长度不正确")
	}
}

func (p *account) GetUsername() string{
	return p.username
}


func (p *account) SetPassword(password string) {
	if len(password)  == 6 {
		p.password = password
	}else{
		fmt.Println("密码长度不正确")
	}
}
func (p *account) GetPassword() string{
	return p.password
}

func (p *account) SetSal(sal float64) {
	if int(sal)  >= 20 {
		p.sal = sal
	}else{
		fmt.Println("金额不正确")
	}
}
func (p *account) GetSal() float64{
	return p.sal
}


main.go

package main

import (
	"fmt"
	"test/model"
)

func main() {
    p := model.NewPerson("","",0.0)  //初始化工厂函数

	p.SetUsername("ewqewq123")        //通过工厂函数修改 用户的账户、密码、余额
	p.SetPassword("123456")
    p.SetSal(5000)

    //查看信息
	fmt.Println(*p)
    fmt.Println(p.GetUsername())
    fmt.Println(p.GetPassword())
    fmt.Println(p.GetSal())

}

返回

{ewqewq123 123456 5000}
ewqewq123
123456
5000

或者使用下面的方法

mode.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  //返回nil表示创建识别
	}

	if len(pwd)  != 6{
		fmt.Println("密码长度不对")
		return nil
	}

	if balance < 20 {
		fmt.Println("余额数目不对")
		return nil
	}

	return &account{
		accountNo : accountNo,
		pwd : pwd,
		balance : balance,
	}
}



//方法
//1 存款
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("存款成功")
}


//2. 取款
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.Println("取款成功")
}


//3 查询余额
func (account *account) Query(pwd string){
	//看下输入的密码是否正确
	if pwd != account.pwd {
		fmt.Println("你输入的密码不正确")
		return
	}

	fmt.Printf("您的账号为=%v 余额=%v\n",account.accountNo,account.balance)

}




// 获取所有字段的值的方法 Get
func (account *account) GetaccountNo() string {
	return account.accountNo
}
func (account *account) Getpwd() string {
	return account.pwd
}
func (account *account) Getbalance() float64 {
	return account.balance
}

main.go

package main

import (
	"fmt"
	"test/model"
)

func main()  {
  account := model.NewAccount("ewqewq123","123456",3000)

  // 判断结构体不为nil  说明创建成功
  if account != nil {
  	fmt.Println("account 创建成功")
  }else {
  	fmt.Println("创建失败")
  }

  //存款   输入存款金额,密码信息
  account.Deposite(1000,"123456")

  //取款   取款金额  密码信息
  account.WithDraw(1,"123456")

  //查询余额  账号名称
  account.Query("123456")


  //获取三个字段
  fmt.Println(account.GetaccountNo())
  fmt.Println(account.Getpwd())
  fmt.Println(account.Getbalance())

}

返回

account 创建成功
存款成功
取款成功
您的账号为=ewqewq123 余额=3999
ewqewq123
123456
3999