Head First Go -第10部分封装和嵌入


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





如有错误,欢迎指出!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值