golang学习笔记21-面向对象(三):封装与继承【重要】

本节也是GO核心部分,很重要。
注:本人已有C,C++,Python基础,只写本人认为的重点。由于导包语句已经在19讲(笔记19:面向对象的引入)展示过了,所以这里就不展示了。

一、封装

【1】什么是封装:
封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部程序的其它包只有通过被授权的操作方法,才能对字段进行操作。
【2】封装的好处:
1)隐藏实现细节
2)提可以对数据进行验证,保证安全合理
【3】Golang中如何实现封装:
1)建议将结构体、字段(属性)的首字母小写(其它包不能使用,类似private,实际开发不小写也可能,因为封装没有那么严格)
2)给结构体所在包提供一个工厂模式的函数,首字母大写(类似一个构造函数)
3)提供一个首字母大写的Set方法(类似其它语言的public),用于对属性判断并赋值func (var结构体类型名)SetXxx(参数列表){//加入数据验证的业务逻辑var.Age =参数
}
4)提供一个首字母大写的Get方法(类似其它语言的public),用于获取属性的值func (var结构体类型名)GetXxx()(返回值列表){
return var.字段;
}
示例:
在utils中定义结构体和方法:

type person struct {
	Name string
	age  int //其他包不能直接访问
}

//相当于构造器
func NewPerson(name string) *person {
	return &person{
		Name: name,
	}
}

func (p *person) SetAge(age int) {
	if age < 0 || age > 150 {
		fmt.Println("请输入合理的年龄!")
		return
	}
	p.age = age
}
func (p *person) GetAge() int {
	return p.age
}

然后在main中调用:

func main() {
	// 使用构造器创建一个新的 person 实例
	p := utils.NewPerson("李华")

	// 设置年龄
	p.SetAge(30)

	// 获取年龄并打印
	fmt.Printf("姓名: %s, 年龄: %d\n", p.Name, p.GetAge())

	// 尝试设置不合理的年龄
	p.SetAge(200) // 输出: 请输入合理的年龄!

	// 再次获取年龄并打印
	fmt.Printf("姓名: %s, 年龄: %d\n", p.Name, p.GetAge())
}

二、继承

继承的概念和C++一样,就不做赘述了,但GO其实是弱化了这个概念,没有专门的机制来实现继承,具体来说,没有父类子类的概念,没有继承关键字,但可以通过匿名结构体实现。首先,在utils中定义结构体和方法:

type Animal struct {
	Age    int
	Weight float64
}

func (a *Animal) ShowInfo() {
	fmt.Println("年龄=", a.Age, "体重=", a.Weight)
}
func (a *Animal) Shout() {
	fmt.Println("我是动物,我会喊叫")
}

//用Animal是为了提高复用性,显然Cat是Animal的子类
type Cat struct {
	Animal
}

func (c *Cat) Scratch() {
	fmt.Println("我是小猫,我可以挠人")
}

这里cat中的animal显然就是匿名结构体,即没有名字,只有类型的结构体,属于cat的父类。
定义好子类和父类后,在main中调用:

func main() {
	c := &utils.Cat{}
	//方式1
	c.Animal = utils.Animal{Age: 3, Weight: 10.6}
	// 方式2:c.Animal.Age, c.Animal.Weight = 3, 10.6
	c.Animal.ShowInfo()
	c.Animal.Shout()
	c.Scratch()
}

一些细节:
【1】结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法,都可以使用。读者可自行尝试,这里不做赘述。
【2】匿名结构体字段访问可以简化。例:

func main() {
	c := &utils.Cat{}
	c.Age, c.Weight = 3, 10.6
	c.ShowInfo()
	c.Shout()
	c.Scratch()
}

上述程序输出和通过animal访问是一样的,这证明了子类完全继承了父类的方法和字段,这样使用起来就方便很多。
【3】当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分。这句话说白了就是子类和父类变量冲突,比如cat也有一个Age,优先用子类的,如果仍然想用父类的,那么字段访问就不能简化,要显示调用父类。读者可自行尝试,这里不做赘述。
【4】 Golang中支持多继承:如一个结构体嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承。为了保证代码的简洁性,建议大家尽量不使用多重继承,很多语言就将多重继承去除了,但是Go中保留了。多重继承的示例如下:

type A struct {
	Aa int
	Ab string
}
type B struct {
	Bc int
	Bd string
	Aa int
}
type C struct {
	A
	B
}

C的创建语句是c:=C{A{},B{}}
显然,C有两个父类,更多测试代码就不写了。
【5】如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分。比如上一点的A和B中有相同字段,那么和第三点一样,显示调用父类即可。
【6】结构体的匿名字段可以是基本数据类型。也就是说,可以在子类里面写int,float等基本数据类型,调用时就类似c.int,c.float64,…,这个了解即可。
【7】嵌入匿名结构体的指针也是可以的。如果嵌入指针,比如cat里面嵌入*animal,那么创建cat时,要在animal前加取地址符&(与号),使用时用*解引用即可。
【8】结构体的字段可以是结构体类型的(组合模式)。这个其实就是二重结构体,除了访问时还可以再“点”一下,和其他类型没什么区别,当然,这不是继承。示例:
utils内的定义:

type D struct {
	Da int
	Db string
	Dc B //组合模式
}

main中的调用:

func main() {
	d := utils.D{10, "ooo", utils.B{66, "ppp", 99}}
	fmt.Println(d)
	fmt.Println(d.Dc.Bd)
}

程序输出为:

{10 ooo {66 ppp 99}}
ppp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术卷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值