文章目录
1. 创建一个 Date struct
package main
import "fmt"
type Date struct {
Year int
Month int
Day int
}
func main() {
date := Date{Year: 2022, Month: 5, Day: 3}
fmt.Printf("date: %v\n", date) //date: {2022 5 3}
}
现在问题来了,就是我们赋值的时候是希望说对于那些不合法的数据要检测的,但是像上面的方法没有对这些错误检测,而是直接赋值了,所以下面我们引入setter
2. setter方法
我们创建setter方法,并且使用指针进行参数的赋值,用指针才可以达到修改的目的
package main
import "fmt"
type Date struct {
Year int
Month int
Day int
}
func (d *Date) SetYear(year int){
d.Year = year
}
func (d *Date) SetMonth(month int){
d.Month = month
}
func (d *Date) SetDay(day int){
d.Day = day
}
func main() {
date := Date{}
date.SetDay(12)
date.SetMonth(12)
date.SetYear(12)
fmt.Printf("date: %v\n", date) //date: {12 12 12}
}
现在我们就可以看到三个set方法了,其实这么一来,我们就可以和 java 一样,对于一个结构体使用这种方式也封装成一个 bean 了
3. 在setter方法中添加校验
在set方法中简单做一下参数校验
package main
import (
"errors"
"fmt"
)
type Date struct {
Year int
Month int
Day int
}
func (d *Date) SetYear(year int) error{
if year < 0{
return errors.New("不合法的年份")
}
d.Year = year
return nil
}
func (d *Date) SetMonth(month int) error{
if month < 0 || month > 12{
return errors.New("不合法的月")
}
d.Month = month
return nil
}
func (d *Date) SetDay(day int) error{
if day < 0 || day > 31{
return errors.New("不合法的日")
}
d.Day = day
return nil
}
func main() {
date := Date{}
err := date.SetDay(188)
if err != nil{
fmt.Printf("err.Error(): %v\n", err.Error())
}
err = date.SetMonth(12)
if err != nil{
fmt.Printf("err.Error(): %v\n", err.Error())
}
err = date.SetYear(12)
if err != nil{
fmt.Printf("err.Error(): %v\n", err.Error())
}
fmt.Printf("date: %v\n", date) //date: {12 12 12}
}
4. 设置为未导出进一步提高安全性
现在我们知道可以通过上面的set方法来设置了,但是我们还是可以通过.
运算来对结构体进行赋值,那么有没有什么办法呢?我们知道,如果一个结构体或者其他的属性要被使用,那么首字母就要大写,针对这种写法,我们就把结构体中的属性都变成小写开头。
我们把结构体和set方法抽出来单独作为一个文件
package calendar
import(
"errors"
)
type Date struct {
year int
month int
day int
}
func (d *Date) SetYear(year int) error {
if year < 0 {
return errors.New("不合法的年份")
}
d.year = year
return nil
}
func (d *Date) SetMonth(month int) error {
if month < 0 || month > 12 {
return errors.New("不合法的月")
}
d.month = month
return nil
}
func (d *Date) SetDay(day int) error {
if day < 0 || day > 31 {
return errors.New("不合法的日")
}
d.day = day
return nil
}
然后我们导入这个包,使用结构体,可以看到直接.
是不可以赋值了,所以就解决了这个问题
package main
import (
"fmt"
"log"
"mycode/calendar"
)
func main() {
var date calendar.Date
err := date.SetDay(14)
if(err != nil){
log.Fatal(err)
}
err = date.SetMonth(50)
if(err != nil){
log.Fatal(err)
}
err = date.SetYear(2022)
if(err != nil){
log.Fatal(err)
}
fmt.Println(date)
}
5. getter方法获取属性值
好了,现在我们已经设置了setter,但是经过这么修改之后我们发现访问不到了,所以现在我们提供getter方法来对这些属性进行访问。
//返回year
func (d * Date) Year() int{
return d.year
}
//返回月
func (d * Date) Month() int{
return d.month
}
//返回日
func (d * Date) Day() int{
return d.day
}
注意我们这里返回的方法不是get什么,而是和字段名相同,首字母大写,Go社区在一个大会上决定了不需要使用 “get” + 字段名的方式,但是对于set还是需要保留 "set"前缀,当然你也可以保留 “get”,只是不建议这么做。
package calendar
import (
"errors"
)
type Date struct {
year int
month int
day int
}
//返回year
func (d *Date) Year() int {
return d.year
}
//返回月
func (d *Date) Month() int {
return d.month
}
//返回日
func (d *Date) Day() int {
return d.day
}
func (d *Date) SetYear(year int) error {
if year < 0 {
return errors.New("不合法的年份")
}
d.year = year
return nil
}
func (d *Date) SetMonth(month int) error {
if month < 0 || month > 12 {
return errors.New("不合法的月")
}
d.month = month
return nil
}
func (d *Date) SetDay(day int) error {
if day < 0 || day > 31 {
return errors.New("不合法的日")
}
d.day = day
return nil
}
package main
import (
"fmt"
"log"
"mycode/calendar"
)
func main() {
var date calendar.Date
err := date.SetDay(14)
if err != nil {
log.Fatal(err)
}
err = date.SetMonth(5)
if err != nil {
log.Fatal(err)
}
err = date.SetYear(2022)
if err != nil {
log.Fatal(err)
}
fmt.Printf("date.Day(): %v\n", date.Day()) //date.Day(): 14
fmt.Printf("date.Month(): %v\n", date.Month()) //date.Month(): 5
fmt.Printf("date.Year(): %v\n", date.Year()) //date.Year(): 2022
}
这种写法就是封装了,在Go中通常都是字段数据需要的时候才封装,比如需要set校验,如果我们不需要封装一个字段,通常导出并允许直接访问
6. 在Event类型中嵌入Date类型
我们定义一个Event,里面包括了Date类型。
package calendar
type Event struct {
Title string
Date
}
在第8章的时候这样写可以直接通过exent访问到date里面的字段,这是一种匿名字段的写法。但是这里却不可以
package main
import "mycode/calendar"
func main() {
var event calendar.Event
event.Title = "aaa"
event.month = 5
//event.month undefined (type calendar.Event has no field or method month, but does have Month)
}
其实原因也很简单,因为我们没有首字母大写,属性就是不可导出的,所以我们没有办法直接访问,所以还是一样的按照上面的方法来设置get 和 set方法就可以访问到了。这里也说明了一件事,从Date里面提升属性为导出,在外部类型中也是同样可以使用的。
package main
import (
"fmt"
"mycode/calendar"
)
func main() {
var event calendar.Event
event.Title = "aaa"
event.SetDay(1)
event.SetMonth(2)
event.SetYear(3)
fmt.Printf("event: %v\n", event) //event: {aaa {3 2 1}}
}
如有错误,欢迎指出!!!