Go语言结构体(struct)面向对象编程进阶篇(封装,继承和多态)

news/2024/9/23 15:22:41/文章来源:https://www.cnblogs.com/yinzhengjie/p/18331403

                                              作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

目录
  • 一.Go语言的封装(encapsulation)实现
    • 1.什么是封装(encapsulation)
    • 2.封装(encapsulation)的好处
    • 3.golang如何实现封装(encapsulation)
    • 4.代码实现
      • 4.1 代码组织结构
      • 4.2 创建go.mod文件
      • 4.3 dongman.go
      • 4.4 main.go
  • 二.Go语言的继承(inheritance)实现
    • 1.继承(inheritance)概述
    • 2.继承案例代码实现
    • 3.Golang支持多继承
    • 4.嵌套匿名结构体指针
    • 5.组合模式(嵌套结构体)并非继承
    • 6.嵌套结构体的字段名冲突
  • 三.Go语言的多态(polymorphic)实现
    • 1.多态概述
    • 2.多态案例
  • 四.结构体内存布局
    • 1 结构体占用一块连续的内存
    • 2 空结构体
    • 3 内存对齐[了解即可]

一.Go语言的封装(encapsulation)实现

1.什么是封装(encapsulation)

封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起。数据被保护在内部,程序的其他包只有通过被授权的操作方法,才能对字段进行操作。

2.封装(encapsulation)的好处

- 1.隐藏实现细节;- 2.可以对数据进行校验,保证安全合理;

3.golang如何实现封装(encapsulation)

- 1.建议将结构体,字段(属性)的首字母小写(其他包不能使用,类似private,实际开发不小写也可能,因为封装没有那么严格);- 2.给结构体所在的包提供一个工程模式函数,首字母大写(类似于一个构造函数);- 3.提供一个首字母大写的set方法(类似于其他语言的public),用于对属性判断并赋值- 4.提供一个首字母大写的Get方法(类似于其他语言的publiic),用于获取属性的值;

4.代码实现

4.1 代码组织结构

如上图所示,代码组织结构

4.2 创建go.mod文件

yinzhengjie@bogon 12-encapsulation % go mod init yinzhengjie-fengzhuang
go: creating new go.mod: module yinzhengjie-fengzhuang
yinzhengjie@bogon 12-encapsulation % 
yinzhengjie@bogon 12-encapsulation % ls
go.mod
yinzhengjie@bogon 12-encapsulation % 
yinzhengjie@bogon 12-encapsulation % cat go.mod 
module yinzhengjie-fengzhuanggo 1.22.4
yinzhengjie@bogon 12-encapsulation % 

4.3 dongman.go

package dongmanimport "fmt"type dongMan struct {Name string// 此处为故意将age,hobby字段设置为小写,这意味着其他包无法直接访问这两个属性。age    inthobby  []stringLeader string
}func (d dongMan) String() string {return fmt.Sprintf("[%s]的男主是[%s],在[%d]岁时修炼到元婴,我的爱好是: %s", d.Name, d.Leader, d.age, d.hobby)
}// 定义工程模式函数,相当于其他Java和Python等编程的构造器
func NewDongMan(name string, age int, leader string, hobby []string) *dongMan {return &dongMan{Name:   name,age:    age,Leader: leader,hobby:  hobby,}
}// 定义set方法,对age字段进行封装,因为在方法中可以添加一系列的限制操作,确保被封装字段的安全合理性
func (d *dongMan) SetAge(age int) {// 通过set方法,我们可以设置age的范围,否则外部就可以直接对age字段进行赋值if age > 0 && age < 1000 {d.age = age} else {fmt.Printf("元婴期修士寿命范围在0~1000岁,您传入的[%d]不合法\n", age)}
}// 定义get方法,用于外部包获取隐藏的字段
func (d *dongMan) GetAge() int {return d.age
}

4.4 main.go

package mainimport ("fmt""yinzhengjie-fengzhuang/dongman"
)func main() {// 创建dongMan结构体d := dongman.NewDongMan("《凡人修仙传》", 217, "韩立", []string{"养灵虫", "制傀儡", "炼丹", "修炼", "阵法"})fmt.Println(d)// 跨包无法访问小写字母的属性字段// d.age = 400// d.hobby = []string{"韩跑跑", "捡破烂"}// 由于我们将age属性封装起来了,想要访问该字段,则需要通过SetAge方法进行修改,如果字段不合法则不会设置成功哟~// d.SetAge(2000) d.SetAge(300)// 由于我们将age属性封装起来了,想要访问该字段,则需要通过GetAge方法进行查看fmt.Printf("我是[%s]男主[%s],今年[%d]岁~\n", d.Name, d.Leader, d.GetAge())}

二.Go语言的继承(inheritance)实现

1.继承(inheritance)概述

当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法,其他的结构体不需要重新定义这些属性和方法,只需嵌套一个匿名结构体即可。换句话说, 在Golang中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,这就是所谓的继承。如上图所示,继承的优点就是提高了代码的复用性和扩展性,多个结构体无需重复定义属性和方法,仅需关系各自结构体的方法即可。Golang使用继承注意事项:- 1.结构体可以使用嵌套匿名结构体所有的字段和方法,包括首字母大写或者小写的字段,方法,都可以使用;- 2.匿名字段结构体字段访问可以简化;- 3.当结构体和匿名结构体有相同的字段或者方法时,编译器采用"就近访问"原则访问,如系统访问匿名结构体的字段和方法,可以通过匿名结构体来区分;- 4.Golang中支持多继承,如一个结构体嵌套了多个匿名结构体,那么该结构体可以访问直接嵌套的你们结构体的字段和方法,从而实现了多重继承;- 5.如嵌入的匿名结构体有相同的字段名或者方法名称,则在访问时,需要通过匿名结构体类型名来区分;- 6.结构体的匿名字段可以是基础数据类型,调用时基于该基础数据类型调用即可;- 7.在创建嵌套匿名结构体变量(实例)时,可以直接指定各个匿名结构体字段的值;- 8.嵌入匿名结构体的指针也是可以的;- 9.结构体的字段可以是结构体类型的(组合模式,嵌套结构体),但这种写法并不属于继承关系,只是属于该结构体的一个字段的类型而已;温馨提示:为了保证代码的简洁性,建议大家尽量不使用多重继承,很多语言就将多重继承去除了,但是Go中保留了。

2.继承案例代码实现

package mainimport "fmt"// Animal结构体为表示动物,是其他结构体的"父结构体"
type Animal struct {Name   stringAge    intWeight float64
}// 给Animal绑定Speark方法
func (a *Animal) Speark() {fmt.Printf("%s又开始叫唤了...", a.Name)
}// 给Animal绑定Show方法
func (a *Animal) Show() {fmt.Printf("%s的今年%d岁,体重是%.2fkg\n", a.Name, a.Age, a.Weight)
}// Bird结构体表示鸟,属性字段继承自Animal
type Bird struct {// 为了复用性,体现继承思维,加入匿名结构体Animal
}// 为Bird结构体绑定特有的方法
func (b *Bird) Fight() {fmt.Printf("快看,%s又飞起来啦~\n", b.Name)
}// Dog结构体表示狗,属性字段继承自Animal
type Dog struct {Animal
}// 定义Dog结构体特有的方法
func (d *Dog) Run() {fmt.Printf("%s狗子,跑的真快\n", d.Name)
}func (d *Dog) Jump() {fmt.Printf("%s狗子,跳的好高\n", d.Name)
}type Cat struct {Animal
}func (c *Cat) Scratch() {fmt.Printf("%s猫咪,又开始抓人了\n", c.Name)
}func main() {bird := &Bird{}bird.Animal.Name = "小黄"bird.Animal.Age = 1bird.Animal.Weight = 0.2dog := &Dog{}dog.Animal.Name = "雪花"dog.Animal.Age = 3dog.Animal.Weight = 4cat := &Cat{}cat.Animal.Name = "大花"cat.Animal.Age = 4cat.Animal.Weight = 2bird.Speark()bird.Show()bird.Fight()dog.Speark()dog.Show()dog.Run()dog.Jump()cat.Speark()cat.Show()cat.Scratch()}

3.Golang支持多继承

package mainimport "fmt"type Father struct {Name stringAge  int
}type Mother struct {Name string
}type Son struct {Name string// 结构体的匿名字段可以是基础数据类型,这种没有名字的字段就称为匿名字段,调用时基于该基础数据类型调用即可;//这里匿名字段的说法并不代表没有字段名,而是默认会采用类型名作为字段名;// 结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。int// 继承多个结构体,尽管Go语言支持多继承,但推荐少用。很多语言就将多重继承去除了,因为容易造成逻辑混乱。FatherMother
}func (f *Father) DriveCar() {fmt.Printf("%s开车很稳~\n", f.Name)
}func (m *Mother) Sing() {fmt.Printf("%s唱歌很好听~\n", m.Name)
}func (s *Son) Dance() {fmt.Printf("%s跳舞很好看\n", s.Name)
}func main() {// 构建Son结构体实例// s := Son{"唐三", 18, Father{"唐昊", 30}, Mother{"阿银"}}s := Son{"唐三",18,// 在创建嵌套匿名结构体变量(实例)时,可以直接指定各个匿名结构体字段的值;Father{Name: "唐昊",Age:  30,},Mother{Name: "阿银",},}fmt.Printf("s = %v\n", s)// 通过Son结构体实例的确可以调用多个继承结构体的方法s.Sing()s.Dance()s.DriveCar()// 如嵌入的匿名结构体有相同的字段名或者方法名称,则在访问时,需要通过匿名结构体类型名来区分;fmt.Printf("[%s]的今年[%d]岁, 父亲是: [%s], 今年[%d]岁, 母亲是: [%s]\n", s.Name, s.int, s.Father.Name, s.Age, s.Mother.Name)
}

4.嵌套匿名结构体指针

package mainimport "fmt"type Father struct {Name stringAge  int
}type Mother struct {Name string
}type Son struct {Name stringint// 嵌入匿名结构体的指针也是可以的*Father*Mother
}func (f *Father) DriveCar() {fmt.Printf("%s开车很稳~\n", f.Name)
}func (m *Mother) Sing() {fmt.Printf("%s唱歌很好听~\n", m.Name)
}func (s *Son) Dance() {fmt.Printf("%s跳舞很好看\n", s.Name)
}func main() {// 构建Son结构体实例s := Son{"唐三", 18, &Father{"唐昊", 30}, &Mother{"阿银"}}fmt.Printf("s = %v\n", s)s.Sing()s.Dance()s.DriveCar()fmt.Printf("[%s]的今年[%d]岁, 父亲是: [%s], 今年[%d]岁, 母亲是: [%s]\n", s.Name, s.int, s.Father.Name, s.Age, s.Mother.Name)
}

5.组合模式(嵌套结构体)并非继承

package mainimport ("fmt"
)type Address struct {Province stringCity     string
}// 一个结构体中可以嵌套包含另一个结构体或结构体指针,我们也称之为"组合模式"。
type User struct {Name   stringGender string// 结构体的字段可以是结构体类型的(组合模式),但这种写法并不属于继承关系,只是属于该结构体的一个字段的类型而已;Address Address
}func main() {user1 := User{Name:   "JasonYin",Gender: "男",Address: Address{Province: "陕西",City:     "安康",},}fmt.Printf("user1=%#v\n", user1)
}

6.嵌套结构体的字段名冲突

package mainimport ("fmt"
)// Address 地址结构体
type Address struct {Province   stringCity       stringCreateTime string
}// Email 邮箱结构体
type Email struct {Account    stringCreateTime string
}// User 用户结构体
type User struct {Name   stringGender stringAddressEmail
}func main() {var u1 Useru1.Name = "Jason Yin"u1.Gender = "男"u1.Province = "陕西"u1.City = "安康"u1.Account = "y1053419035@qq.com"// 由于2个匿名字段都有CreateTime字段,因此无法省略匿名字段哟!// u1.CreateTime = "2020"// 嵌套结构体内部可能存在相同的字段名。在这种情况下为了避免歧义需要通过指定具体的内嵌结构体字段名。u1.Address.CreateTime = "2021"u1.Email.CreateTime = "2025"fmt.Println(u1)
}

三.Go语言的多态(polymorphic)实现

1.多态概述

变量(实例)具有多种形态,在Go语言中,多态特征是通过接口实现的。可以按照统一的接口来调用不同的接口实现。这时接口变量就呈现出不同的形态。

2.多态案例

package mainimport "fmt"// 定义SayHi接口
type SayHi interface {SayHello()
}type Chinese struct {Name string
}type American struct {Name string
}func (c Chinese) SayHello() {fmt.Printf("你好,我的名字是: %s, 很高兴认识你。\n", c.Name)
}func (a American) SayHello() {fmt.Printf("Hi,My name is %s, And you ?\n", a.Name)
}// 定义一个函数,专门用来各国人打招呼的函数,接受具备SayHi接口能力的变量
// 此处的s(多态参数)可以通过上下文来识别具体是什么类型的实例,此时就体现出"多态"
func greet(s SayHi) {s.SayHello()
}func main() {// 定义多态数组var array [3]SayHiarray[0] = Chinese{"女娲"}array[1] = American{"超人"}array[2] = Chinese{"夸父"}fmt.Println(array)// 遍历接口,调用接口,体现出多态的效果for _, item := range array {greet(item)}
}

四.结构体内存布局

1 结构体占用一块连续的内存

package mainimport ("fmt""unsafe"
)// 结构体占用一块连续的内存。
type test struct {a int8b int8c int8d int8
}func main() {n := test{11, 22, 33, 44,}fmt.Printf("n.a %p\n", &n.a)fmt.Printf("n.b %p\n", &n.b)fmt.Printf("n.c %p\n", &n.c)fmt.Printf("n.d %p\n", &n.d)// 查看占用的空间大小fmt.Println(unsafe.Sizeof(n))
}

2 空结构体

package mainimport ("fmt""unsafe"
)func main() {// 空结构体是不占用空间的。var v struct{}// 查看占用的空间大小fmt.Println(unsafe.Sizeof(v))
}

3 内存对齐[了解即可]

关于Go语言中的内存对齐推荐阅读:https://www.liwenzhou.com/posts/Go/struct-memory-layout/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/773888.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决历理 如何用cmd运行小键盘

cmd不可以直接运行键盘上的按键,但是可以打开电脑上的软键盘来对应键盘键。 打开软键盘来运行键盘上的按键步骤如下: 1、点击Windows徽标键和R键,在出现的窗口中输入cmd; 2、点击确定,弹出命令提示符窗口; 3、在命令提示符窗口输入osk,点击键盘上的Enter,屏幕上就会出现…

ARGOCD用户管理

1.创建用户alice kubectl apply -f argocd-cm.yamlapiVersion: v1 kind: ConfigMap metadata:name: argocd-cmnamespace: argocdlabels:app.kubernetes.io/name: argocd-cmapp.kubernetes.io/part-of: argocd data:# add an additional local user with apiKey and login capab…

全网最适合入门的面向对象编程教程:27 类和对象的Python实现-Python中异常层级与自定义异常类的实现

本文主要介绍了在使用Python进行面向对象编程时,异常的层级和如何使用继承关系完成自定义自己项目中异常类,并以传感器数据采集为例进行讲解。全网最适合入门的面向对象编程教程:27 类和对象的 Python 实现-Python 中异常层级与自定义异常类的实现摘要: 本文主要介绍了在使…

初识WPF

1.新建wpf程序,app.xaml文件说明: 2.Attribute特征:为了表示同类标签中的某个标签阈值不同,可以给它的特征赋值: 3.命名空间的引用:使用xmlns特征来定义名称空间:语法格式为:xmlns[:可选的映射前缀]=“命名空间”,如果没有写映射前缀,说明是默认的命名空间: 4.x:Cl…

认知觉醒-1-大脑的构造

最近重新开始审视自己的生活和规划,无意间在微信读书上看到了《认知觉醒》这本书评价挺不错的,所以通过听书和看书的方式进行了学习,感觉对自己的现状很有启发,简单记录一下自己的读书心得。 大脑的构造导致了你就是不爱学习 本书提到,人类的大脑是由三部分构成的,可以分…

前端断点及使用

原文链接:https://www.cnblogs.com/sunny3158/p/17797552.html 一、打断点的方式1.找到源码位置,添加断点(1)寻找源码位置①如果是正常html页面,那么源码一般是在对应域名下面。如果是webpack处理的页面,并且开启了源码映射,源码就是在webpack://下面。可通过快捷键ctrl…

emojiCTF2024

emojiCTF2024emojiCTF2024 WEB http 题目: ​​ 思路:修改 UA 头为 EMOJI_CTF_User_Agent_v1.0:User-Agent: EMOJI_CTF_User_Agent_v1.0​ 修改 http 方法,试了一下,修改成 PUT,可以 添加一个自定义头部,EMOJI-CTF-Auth: Passw0rd!​ 抓包修改就行,成功后在路径上加个 …

2024 年巴黎奥运会 All In One

2024 年巴黎奥运会 All In One2024 年巴黎奥运会 All In One https://olympics.com/zh/paris-2024 奖牌统计 - 2024年巴黎奥运会奖牌榜 https://olympics.com/zh/paris-2024/medals https://olympics.com/zh/paris-2024/medals/medallistsmewatch https://www.mewatch.sg/paris…

7.29.01

fish = 1while True:temp, YES = fish, Truefor _ in range(5):if (temp-1)%5 == 0:temp = (temp-1)//5 * 4else: YES = Falseif YES:print(fish)breakelse: fish += 13121

低空经济

思维导图:低空经济

痞子衡嵌入式:MCUXpresso IDE下在线联合调试i.MXRT1170双核工程的三种方法

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是MCUXpresso IDE下在线联合调试i.MXRT1170双核工程的三种方法。两年前痞子衡写过一篇《i.MXRT1170下在线联合调试双核工程的三种方法(IAR篇)》,那篇文章详细介绍了 IAR 下调试 RT1170 双核工程的几种方法。现…

【基础】递归问题—汉诺塔 - 题解

【基础】递归问题—汉诺塔时间限制:C/C++ 1000MS,其他语言 2000MS 内存限制:C/C++ 127MB,其他语言 254MB描述 汉诺塔(又称河内塔)问题是印度的一个古老的传说。开天辟地的神勃拉玛在一个庙里留下了三根金刚石的棒,第一根上面套着 \(64\) 个圆的金片,最大的一个在底下,…