Golang的面向对象_go结构体 增加属性-程序员宅基地

技术标签: 面向对象  Golang设计模式  golang  

如你所知,封装、继承、多态和抽象是面向对象编程的4个基本特征,本文描述Golang语言是如何实现这些特征的。

1 Golang的面向对象类型

Golang实现面向对象的两个关键类型是struct和interface,其中struct类似C++的普通类类型,interface则对应抽象类类型。与C++采用public/protected/private指示成员和方法的可见性不同,Golang采用大小写标识可见性,即大写字母开头的成员/方法对外可见,小写开头的则属于类的私有成员,外部不可以直接访问。此外,Golang与C++在类类型的定义上还有一个重要区别,那就是Golang在struct内只需要声明类的成员变量,而不需要在类定义体内声明或定义所有的方法,方法定义都在struct之外完成。好了,我们开始正文。

2 Golang的面向对象实现

2.1 封装

学生有姓名、年龄和专业等属性,于是我们定义一个Student类型如下:

type Student struct {
    
	name  string
	age   int
	major string
}

学生可以跟大家打招呼:

func (s Student) SayHi() {
    
	fmt.Printf("Hi, I am %s aged %d, and my major is %s\n", s.name, s.age, s.major)
}

在函数定义的func关键字后面加上我们定义的Student类型变量定义,这个函数就成为了Student的方法。类图表示如下:

encapsulate

值得注意的是,在Golang内,除slice、map、channel和显示的指针类型属于引用类型外,其它类型都属于值类型,前者作为函数入参传递时,函数对参数的修改会影响调用对象,而后者作为入参时,函数体内会生成调用对象的拷贝,函数对入参的修改不会影响调用对象。因此,如果我们要给Student类定义一个“构造函数”,我们希望的是这个函数的入参可以被赋值到Student的成员内,则该“构造函数”应该使用指针类型对象定义:

func (s *Student) Init(name string, age int, major string) {
    
	s.name = name
	s.age = age
	s.major = major
}

我们来测试一下:

s := Student{
    }
s.Init("pirlo", 21, "cs")
s.SayHi()

输出结果:

$ go run test_encapsulate.go 
Hi, I am pirlo aged 21, and my major is cs.

我们定义的学生类型,属性都是私有的,方法都是公有的,还记得么,私有或公有都是通过属性或方法的首字母大小写决定的。那我们现在来试一下公有属性和私有方法吧。
比如,我想让专业(major)这个属性成为公有属性:

type Student struct {
    
	name  string
	age   int
	Major string
}

func main() {
    
	s := Student{
    }
	s.Init("pirlo", 21, "cs")
	s.SayHi()

	s.Major = "finance"
	s.SayHi()
}

先调用构造函数设置专业为cs,再通过显示赋值的方式修改专业为finance:

$ go run test_encapsulate.go 
Hi, I am pirlo aged 21, and my major is cs.
Hi, I am pirlo aged 21, and my major is finance.

但是如果我们试图修改私有属性:

s.age = 22

编译器会告诉你:

$ go run test_encapsulate.go 
# command-line-arguments
./test_encapsulate.go:15: s.age undefined (cannot refer to unexported field or method age)

括号的注释说明了不能引用未导出/未公开的属性或方法。

小结一下,Golang通过struct定义类的属性,通过在func定义时传入类对象的方式定义类的方法,其中属性和方法的公有/私有属性是通过首字母的大小写决定的。

2.2 继承

与C++、Java等完整支持面向对象的语言不同,Golang没有显式的继承,而是通过组合实现继承。
我们先定义一个基类Person,提供姓名和年龄两个属性,以及SayHi一个方法(Init类似于构造函数):

type Person struct {
    
	name string
	age  int
}

func (p *Person) Init(name string, age int) {
    
	p.name = name
	p.age = age
}

func (p Person) SayHi() {
    
	fmt.Printf("Hi, I am %s, %d years old.\n", p.name, p.age)
}

然后,我们通过组合的方式继承这个基类,实现Employee子类:

type Employee struct {
    
	Person
	company string
}

func (e *Employee) Init(name string, age int, company string) {
    
	e.Person.Init(name, age)
	e.company = company
}

func (e Employee) Work() {
    
	fmt.Printf("I'm working %s.\n", e.company)
}

Employee组合了Person这个成员,除此之外它还拥有自己的成员company,即所属公司,雇员除了是一个Person之外,还需要工作,因此我们定义了Work这个方法。好了,我们再测试一下:

func main() {
    
	p := oo.Person{
    }
	p.Init("pirlo", 21)
	p.SayHi()

	e := oo.Employee{
    }
	e.Init("kaka", 22, "milan")
	e.SayHi()
	e.Work()
}
$ go run test_inherit.go 
Hi, I am pirlo, 21 years old.
Hi, I am kaka, 22 years old.
I'm working in milan.

雇员kaka可以像pirlo一样说话,与此同时,他还可以在milan工作,类图表示如下:

inherit

小结一下,Golang没有完整实现继承,而是通过组合的方式实现。组合类(子类)可以直接调用被组合类(基类)的公有方法,访问基类的公有属性,子类也可以定义自己的属性,以及实现自己特有的方法。Golang的设计哲学之一就是简洁,通过大小写区分成员/方法的公有/私有属性,通过组合的方式实现继承,都是简洁哲学的体现。

2.3 抽象

抽象的反义词是具体,在面向对象编程中,抽象的意思是将共同的属性和方法抽象出来形成一个不可以被实例化的类型,在Java里面,这是通过abstract和interface实现的,其中前者可以包含属性,后者则是纯粹的方法集合;C++通过在类内定义纯虚函数使得该类成为一个抽象类。
Golang的interface类型定义的也是一个抽象的基类,它是一组方法的集合,任何完整实现这些方法的类型都被称为该接口的实现。由于抽象与多态是相辅相成的,或者说抽象的目的就是为了实现多态,我们将在下一节给出实例说明Golang的抽象和多态的实现。

2.4 多态

基类指针可以指向任意派生类的对象,并在运行时绑定最终调用的方法的过程被称为多态。多态是运行时特性,而继承则是编译时特征,也就是说,继承关系在编译时就已经确定了,而多态则可以实现运行时的动态绑定。
小狗和小鸟都是动物,它们都会移动,也都会叫唤。我们把它们共同的方法提炼出来定义一个抽象的接口:

type Animal interface {
    
	Move()
	Shout()
}

虽然小狗和小鸟都会移动,但小狗是用四条腿爬行,小鸟是用翅膀飞行,虽然它们都会叫唤,但是叫唤的方式也不一样:

type Dog struct {
    
}

func (dog Dog) Move() {
    
	fmt.Println("A dog moves with its legs.")
}

func (dog Dog) Shout() {
    
	fmt.Println("wang wang wang.")
}

type Bird struct {
    
}

func (bird Bird) Move() {
    
	fmt.Println("A bird flys with its wings.")
}

func (bird Bird) Shout() {
    
	fmt.Println("A bird shouts.")
}

类图表示如下:

polymorphism

那么,运行时的多态是怎么实现的呢?

func main() {
    
	var animal oo.Animal

	animal = oo.Dog{
    }
	animal.Move()
	animal.Shout()

	animal = oo.Bird{
    }
	animal.Move()
	animal.Shout()
}

如前文所述,基类指针可以指向任意派生类的对象,并在运行时动态绑定最终使用的方法。这里指针是广义上的概念,在C++中是真实的指针,在Java和Golang里面,则可以是一个接口类型的对象。在上面的代码中,我们定义了一个Animal类型的对象,并分别指向Dog和Bird类型的具体对象,并调用Move和Shout方法,它们的运行效果如下:

$ go run test_polymorphism.go 
A dog moves with its legs.
wang wang wang.
A bird flys with its wings.
A bird shouts.

可以看到,通过定义抽象的接口,以及实现接口方法的具体类型的方式,Golang实现了运行时的动态绑定,这就是所谓的抽象与多态。

完整代码:Golang的面向对象

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_37554486/article/details/77404739

智能推荐

理解差分的经典例题——空调-程序员宅基地

文章浏览阅读961次,点赞16次,收藏22次。理解差分

arcgis api 4-程序员宅基地

文章浏览阅读856次,点赞19次,收藏23次。代码能力一定要注重,尤其是很多原理性的代码(之前两次让我写过Node中间件,Promise.all,双向绑定原理,被虐的怀疑人生)!var tdtMap_anno = new WebTileLayer({//天地图注记。var tdtMap_img = new WebTileLayer({//天地图影像。var tdtMap = new WebTileLayer({//天地图。//加载天地图经纬度影像注记。//加载天地图经纬度矢量。//加载天地图经纬度影像。//加载天地图经纬度地形。

相邻字符判相等_如何判断相邻字符串相等-程序员宅基地

文章浏览阅读352次,点赞8次,收藏9次。输入一行字符(长度小于等于1000),判断其中是否存在相邻两个字符相同的情形,若有,则输出该相同的字符并结束程序(只需输出第一种相等的字符即可)。_如何判断相邻字符串相等

VS.NET桌面程序集成EO.WebBrowser.WebView组件,免弹框版_eowebbrowser wpf-程序员宅基地

文章浏览阅读917次,点赞21次,收藏15次。在VS.NET桌面程序中引入Web控件,实现在.NET桌面程序中集成h5页面,并呈现良好的显示效果。去除了EO.WebBrowser.WebView第一次加载时弹框的问题,并且保证视频在EO.WebBrowser.WebView中的正常播放_eowebbrowser wpf

eNSP 静态路由记操作演示_ensp吓一跳地址配置-程序员宅基地

文章浏览阅读1.2k次,点赞3次,收藏7次。eNSP 静态路由记操作演示注释:路由:将数据包从一个网络发送到另一个网络的过程工作原理:需要依靠路由表来进行寻址工作的路由表选择最佳路径:指明了:到达每个子网或者是主机应从路由器的哪个物理端口发送,通过此端口 可到达该路径的下一个路由器的地址路由器:只关心网络的状态,决定最佳路径路由表的形成:&直连:通过链路层协议发现的路由直接连接到路由器..._ensp吓一跳地址配置

android旋转spinner方向,android – Spinner下拉列表和屏幕方向更改问题-程序员宅基地

文章浏览阅读319次。我有一个微调器下拉列表和更改方向的问题.在我的活动中,我显示一个对话框,其中有两个微调器.当向用户显示对话框并且用户更改屏幕方向时,一切正常 – 在我的onPause中,我只是简单地关闭对话框.如果用户点击微调器,则在更改方向之前会向用户显示一个下拉列表.但是当用户没有从下拉列表中选择任何内容时,我遇到了一个错误,只是在更改方向之前将列表展开.我已经尝试了很多东西来解决这个问题(即onSaveIn..._android spinner弹出列表旋转

随便推点

12.5m分辨率DEM数据?解读ASF DAAC的DEM数据--关于地理数据收集与处理的基本工具推荐(5)_asf vertex数据怎么看-程序员宅基地

文章浏览阅读1.3w次,点赞37次,收藏129次。一文为你探秘免费的、开源的、覆盖全面的、ALOS PALSAR 12.5m分辨率的DEM数据。1 ASF DAAC是什么?2 数据的整体介绍与处理流程?3 数据如何下载?4 数据的特征与说明?5 总结重点。_asf vertex数据怎么看

【AI Studio】飞桨图像分类零基础训练营 - 03 - 卷积神经网络基础-程序员宅基地

文章浏览阅读760次。前言:第三天,老师结合ppt文图详细讲解了线性和卷积网络的构建,由简单到复杂的讲解卷积网络的发展。最后结合几个项目加深理解。愈发感觉老师讲的好了。第二天的课听完后还感觉自己什么都懂了,结果轮到自己动手搭建模型时发现自己的无知,所以今天要把各个知识点详尽列举。这里写目录标题【AI Studio】飞桨图像分类零基础训练营 - 03 - 图像分类基础一、建立模型1.神经元2.激活函数3.前馈神经网络 / 前向传播过程———————————————【AI Studio】飞桨图像分类零基础训练营 - 03 .

如何压缩pdf文件大小不损失清晰度?pdf压缩文件怎么压缩最小,pdf格式大小怎么压缩?pdf压缩方法_paf不损失压缩-程序员宅基地

文章浏览阅读1k次。搜索一下:~~~~~~轻云pdf编辑压缩官网打开轻云pdf编辑压缩官网,上传pdf文件,上传完毕后网站会自动压缩。当视频压缩的进度条到达100%后就表示视频压缩好了,这个时候回出现预览压缩后的效果选择,大家如果想预览压缩后的视频效果,大家点击预览视频效果按钮就可以了。以上就是我给大家分享的pdf压缩又小又清晰的解决办法了,最大的优势就是操作简单,使用方便,需要压缩pdf的小伙伴们希望能帮到你们。..._paf不损失压缩

Vivado_DDS IP核_设计与仿真_dds vivado-程序员宅基地

文章浏览阅读4.0k次,点赞6次,收藏85次。本文介绍了Vivado中DDS的使用方法,并进行了仿真测试。_dds vivado

荷兰国旗问题(利用基数排序思想实现)_设有一个仅由红、白、蓝种颜色的色块组成的序列,各种色块的个数是随机的,原始排列-程序员宅基地

文章浏览阅读849次。荷兰国旗问题:设有一个仅由红、白、蓝3种颜色的条块组成的条块序列。请编写一个时间复杂度为O(n)的算法,使得这些条块按红、白、蓝的顺序排好,即排成荷兰国旗图案。算法设计利用链式基数排序,将红、白、蓝3种颜色分配到3个链表上,然后对链表进行收集。具体实现typedef enum{Red,White,Blue}Color;typedef struct ListNode{ Color color; struct ListNode *next;}Node,*LinkList;void FlagA_设有一个仅由红、白、蓝种颜色的色块组成的序列,各种色块的个数是随机的,原始排列

大数据基础平台搭建-(四)HBbase集群HA+Zookeeper搭建_大数据平台搭建hadoop hbase-程序员宅基地

文章浏览阅读424次。HBase集群部署采用HA高可用+完全分布式集群方式搭建。_大数据平台搭建hadoop hbase

推荐文章

热门文章

相关标签