封装和嵌入

  • 创建一个日期struct类型

    创建Date struct类型来保存年月日的值。我们在struct中增加Year、Month和Day字段,每个都是int类型。在main函数中,我们将执行一个快速的测试来测试新类型,使用struct字面量来创建一个Date值,并填充其所有字段。我们仅仅使用Println来输出Date值。

    package main
    
    import "fmt"
    
    type Data struct {
    	Year int64
    	Month int
    	Day int
    }
    func main() {
    	data:=Data{Year: 2020,Month: 1,Day: 1}
    	fmt.Println(data)
    }

    image-20201103095308078

    我们需要的是一种方法,让用户数据在被赋值之前就是合法的。在计算机科学中,称为数据校验。我们需要测试Year被设置为大于或等于1的值,Month被设置为112。Day被设置为131。(是的,有些月份没有31天,但是为了让我们的示例代码保持一个合理的长度,我们仅仅检测它在1到31之间。)


setter方法

我们应该可以在Date类型上创建SetYear、SetMonth和SetDay方法来接收值,判断是否有效,如果有效,设置到struct字段。

package main

import (
	"errors"
	"fmt"
)

type Data struct {
	Year int64
	Month int
	Day int
}
func (date *Data)SetYear(year int64) error{
	if year<=0 {
		return errors.New("year err")
	}
	date.Year=year
	return nil
}
func (date *Data)SetMonth(month int) error {
	if month<1 ||month>12 {
		return errors.New("month err")
	}
	date.Month=month
	return nil
}
func (date *Data)SetDay(day int)error{
	if day<1||day>31 {
		return errors.New("day err")
	}
	date.Day=day
	return nil
}
func main() {
	data:=Data{Year: 2020,Month: 1,Day: 1}
	fmt.Println(data)
	{
		err := data.SetDay(32)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetDay(12)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetMonth(13)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetMonth(3)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetYear(13)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetYear(-9)
		if err!=nil {
			fmt.Println(err)
		}
	}
	fmt.Println(data)
}

image-20201103103732935


导出

我们需要一个方式来保护这些字段,这样使Date类型只能使用setter方法来更新字段。

Go提供了一个方法:我们可以把Date类型移动到另一个包,并将数据字段设置为非导出的。

package page

import "errors"

type Data struct {
	year int64
	month int
	day int
}
func (date *Data)SetYear(year int64) error{
	if year<=0 {
		return errors.New("year err")
	}
	date.year=year
	return nil
}
func (date *Data)SetMonth(month int) error {
	if month<1 ||month>12 {
		return errors.New("month err")
	}
	date.month=month
	return nil
}
func (date *Data)SetDay(day int)error{
	if day<1||day>31 {
		return errors.New("day err")
	}
	date.day=day
	return nil
}
package main

import (
	"awesomeProject/src/page"
	"fmt"
)

func main() {
	data:=page.Data{}
	fmt.Println(data)
	{
		err := data.SetDay(32)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetDay(12)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetMonth(13)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetMonth(3)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetYear(13)
		if err!=nil {
			fmt.Println(err)
		}
	}
	{
		err:=data.SetYear(-9)
		if err!=nil {
			fmt.Println(err)
		}
	}
	fmt.Println(data)
}

image-20201103104244143


getter方法

对比setter方法,给Date类型增加getter方法比较简单。当调用它们的时候,它们除了返回字段值以外不需要做其他的操作。

func (date *Date)GetDay() int {
	return date.day
}
func (date *Date)GetMonth() int {
	return date.month
}
func (date *Date)GetYear() int64 {
	return date.year
}

fmt.Println("day:",data.GetDay())
fmt.Println("month:",data.GetMonth())
fmt.Println("year:",data.GetYear())

image-20201103105411416

封装

将程序中的数据隐藏在一部分代码中而对另一部分不可见的方法称为封装,它不是Go所独有的。封装很有价值,因为它可以用来防止无效数据(就像我们看到的)。同样,你也可以修改程序代码的封装部分,不用担心其他代码的访问,因为它们不可直接访问。

许多其他编程语言用类封装数据。(类与Go的类型概念相似,但不完全相同。)在Go中使用未导出的变量、struct字段、函数或者方法,把数据封装在包中。

Date字段是未导出的,并且Go不会将未导出的字段提升到封闭类型。那说得通,我们确认字段被封装,这样它们就只能被setter和getter方法访问,并且我们不希望封装被字段提升绕开。

package main

import (
	"awesomeProject/src/page"
	"errors"
)

type Per struct {
	page.Date
	text string
}

func (per *Per)SetText(str string) error {
	if len(str)<0 {
		return errors.New("str err")
	}
	per.text=str
	return nil
}
func (per *Per)getText() string {
	return per.text
}
func main() {
	per:=Per{}
	println(per.GetDay())
	if per.SetText("a")!=nil {
		println("err")
	}
	println(per.getText())
}

image-20201103111157703


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

接口 上一篇
定义类型 下一篇