Go 中的 OOP- 用结构体代替类

Go是面向对象的吗?

Go 不是一种纯粹的面向对象编程语言。这段摘录自 Go 的常见问题解答,回答了 Go 是否是面向对象的问题。

是也不是。虽然Go有类型和方法,并且允许面向对象的编程风格,但是没有类型层次结构。Go中的“接口”概念提供了一种不同的方法,我们认为这种方法易于使用,并且在某些方面更通用。还有一些方法可以将类型嵌入到其他类型中,以提供类似于子类化(但不完全相同)的东西。此外,Go中的方法比c++或Java中的方法更通用:它们可以为任何类型的数据定义,甚至可以为内置类型(如普通的“未装箱”整数)定义。它们并不局限于结构体(类)。此外,由于缺乏类型层次结构,Go中的“对象”感觉比c++或Java等语言要轻得多。

在接下来的教程中,我们将讨论如何使用 Go 实现面向对象的编程概念。与其他面向对象语言(例如 Java)相比,其中一些在实现上有很大不同。

使用结构体代替类

Go 不提供类,但它提供结构体。可以在结构上添加方法。这提供了将数据和对数据进行操作的方法捆绑在一起的行为,类似于类。

让我们立即从一个例子开始,以便更好地理解。

我们将在此示例中创建一个自定义包,因为它有助于更好地理解结构如何有效替代类。

在里面创建一个子文件夹~/Documents/并命名oop

让我们初始化一个名为 的 go 模块oop。输入以下命令

go mod init oop  

在里面创建一个子文件夹employee。在employee文件夹内,创建一个名为employee.go

文件夹结构看起来像,

├── Documents
│   └── oop
│       ├── employee
│       │   └── employee.go
│       └── go.mod

请将employee.go的内容替换为以下内容,

package employeeimport (  "fmt"
)type Employee struct {  FirstName   stringLastName    stringTotalLeaves intLeavesTaken int
}func (e Employee) LeavesRemaining() {  fmt.Printf("%s %s has %d leaves remaining\n", e.FirstName, e.LastName, (e.TotalLeaves - e.LeavesTaken))
}

在上面的程序中,第 1 行。指定该文件属于该employee包。

在第7 行声明Employee 结构 ,在14行声明一个名为的方法LeavesRemaining。现在我们有一个结构体和一个对结构体进行操作的方法,它们类似于类一样捆绑在一起。

oop文件夹内命名创建main.go

现在文件夹结构看起来像

├── Documents
│   └── oop
│       ├── employee
│       │   └── employee.go
│       ├── go.mod
│       └── main.go

main.go内容如下

package mainimport "oop/employee"func main() {  e := employee.Employee {FirstName: "Sam",LastName: "Adolf",TotalLeaves: 30,LeavesTaken: 20,}e.LeavesRemaining()
}

我们在第 3 行导入employee包。从第 12 行调用该结构体的方法LeavesRemaining()

该程序将打印输出,

Sam Adolf has 10 leaves remaining  

New() 函数而不是构造函数

我们上面编写的程序看起来不错,但其中有一个微妙的问题。让我们看看当我们用零值定义员工结构时会发生什么。将 main.go的内容替换为以下代码,

package mainimport "oop/employee"func main() {  var e employee.Employeee.LeavesRemaining()
}

我们所做的唯一更改是创建零值Employee。该程序将输出,

  has 0 leaves remaining

正如您所看到的,使用 0 值创建的变量Employee是不可用的。它没有有效的名字、姓氏,也没有有效的休假详细信息。

在其他 OOP 语言(如 java)中,这个问题可以通过使用构造函数来解决。可以使用参数化构造函数创建有效的对象。

Go 不支持构造函数。如果类型的零值不可用,则程序员的工作是取消导出该类型以防止从其他包访问,并提供一个名为NewT(parameters)的函数,该函数用所需的值初始化为类型T

Go 中的约定是命名一个创建NewT(parameters)的函数。这将充当构造函数。如果包只定义了一种类型,那么 Go 中的约定是将此函数命名New(parameters)

让我们对我们编写的程序进行更改,以便每次创建员工时都可用。

第一步是取消导出Employee结构并创建一个函数New()来创建新的Employee. 将代码替换employee.go 为以下内容,

package employeeimport (  "fmt"
)type employee struct {  firstName   stringlastName    stringtotalLeaves intleavesTaken int
}func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee {  e := employee {firstName, lastName, totalLeave, leavesTaken}return e
}func (e employee) LeavesRemaining() {  fmt.Printf("%s %s has %d leaves remaining\n", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken))
}

我们在这里做了一些重要的改变。我们在第 7行将 Employee 结构体的首字母改为小写。那就是我们已经改成type employee struct。通过这样做,我们成功地取消了employee结构的导出并阻止了其他包的访问。最好将未导出结构体的所有字段也设为未导出,除非有特定需要导出它们。由于我们不需要在包employee外的任何地方访问结构体的字段employee,因此我们也取消导出所有字段。

我们在LeavesRemaining()方法中相应地更改了字段名称。

现在,由于employee未导出,因此无法Employee从其他包创建类型的值。因此,我们在第14 行提供了一个导出New函数。它将所需的参数作为输入并返回新创建的员工。

该程序仍然需要进行更改才能使其正常工作,但让我们运行它来了解到目前为止更改的效果。如果运行该程序,它将失败并出现以下编译错误,

# oop
./main.go:6:8: undefined: employee.Employee

这是因为我们的employee包中有一个未导出的employee,文件并且无法从main包中访问它。因此,编译器会抛出一个错误,指出该类型未在main.go 中定义。这正是我们想要的。现在没有其他包能够创建零值employee。我们已成功阻止创建无法使用的员工结构值。现在创建员工的唯一方法是使用该New函数。

main.go的内容替换为以下内容,

package main  import "oop/employee"func main() {  e := employee.New("Sam", "Adolf", 30, 20)e.LeavesRemaining()
}

该文件的唯一更改是第 16 行。 我们通过将所需的参数传递给函数来创建一个新员工New

下面提供了进行所需更改后这两个文件的内容。

employee.go

package employeeimport (  "fmt"
)type employee struct {  firstName   stringlastName    stringtotalLeaves intleavesTaken int
}func New(firstName string, lastName string, totalLeave int, leavesTaken int) employee {  e := employee {firstName, lastName, totalLeave, leavesTaken}return e
}func (e employee) LeavesRemaining() {  fmt.Printf("%s %s has %d leaves remaining\n", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken))
}

main.go

package main  import "oop/employee"func main() {  e := employee.New("Sam", "Adolf", 30, 20)e.LeavesRemaining()
}

运行该程序将输出,

Sam Adolf has 10 leaves remaining  

因此你可以理解,虽然 Go 不支持类,但是可以有效地使用结构体来代替类,并且New(parameters)可以使用签名方法来代替构造函数。

这就是 Go 中的类和构造函数。请留下您的宝贵意见和反馈。

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

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

相关文章

sentinel规则持久化-规则同步nacos-最标准配置

官方参考文档&#xff1a; 动态规则扩展 alibaba/Sentinel Wiki GitHub 需要修改的代码如下&#xff1a; 为了便于后续版本集成nacos&#xff0c;简单讲一下集成思路 1.更改pom 修改sentinel-datasource-nacos的范围 将 <dependency><groupId>com.alibaba.c…

[SUCTF 2019]EasySQL 1

题目环境&#xff1a; 把你的旗子给我&#xff0c;我会告诉你旗子是不是对的。 判断注入类型1回显结果 不是字符型SQL注入 1回显结果 数字型SQL注入 查所有数据库,采用堆叠注入1;show databases;查看所有数据表1;show tables;尝试爆Flag数据表的字段1;show columns from Flag; …

算法——程序的灵魂

文章目录 前言算法与程序的关系例子1&#xff1a;冒泡排序例子2&#xff1a;斐波那契数列算法优化与进阶总结 前言 大家好我是艾老虎尤&#xff0c;算法与程序是计算机科学中两个非常重要的概念。算法是解决问题的方法和步骤&#xff0c;而程序是算法的具体实现。在计算机科学…

LeetCode | 27. 移除元素

LeetCode | 27. 移除元素 OJ链接 这道题有一个方法是要删除的数据直接后一个数据挪动到前一个数据&#xff0c;这个方法好不好&#xff1f;最坏的情况下时间复杂度是O(N^2) 还有一个方法 定义一个src定义一个dst&#xff0c;原地直接进行赋值&#xff0c;不进行挪动&#xf…

Qt程序执行编译输出内容解释

以这个为例&#xff1a; D:\SoftwareInstall\Qt6\Tools\QtCreator\bin\jom\jom.exe -f Makefile.Debug cd AuthorizeTools\ && ( if not exist Makefile D:\SoftwareInstall\Qt6\5.15.2\msvc2019_64\bin\qmake.exe -o Makefile E:\Coding\project\DigitalCamera\digita…

yum安装node,npm

node官网 yum -y install nodejs npm 查看版本 npm -v node -v卸载 yum -y remove nodejs npm修改镜像源 4.1. 修改淘宝镜像源 set registry https://registry.npm.taobao.org npm config get registr4.2. 修改华为云镜像源 npm config set registry https://mirrors.huaweicl…

Vue使用 IndexDB vue操作IndexDB数据库 Vue操作IndexDB数据库

Vue使用 IndexDB vue操作IndexDB数据库 Vue操作IndexDB数据库 Vue使用 IndexDB vue操作IndexDB数据库 Vue操作IndexDB数据库安装 IndexDB类库引入 localForage测试 新增数据、获取数据 Vue使用 IndexDB vue操作IndexDB数据库 Vue操作IndexDB数据库 大部分场景使用 LocalStore都…

【Python】海龟图turtle.color() 方法有关RGB颜色设置详解

在Turtle模块中&#xff0c;turtle.color()函数用于设置画笔和填充颜色&#xff0c;你可以使用RGB颜色码作为参数。RGB颜色码由三个数字组成&#xff0c;分别代表红色&#xff08;R&#xff09;&#xff0c;绿色&#xff08;G&#xff09;和蓝色&#xff08;B&#xff09;的分量…

驱动开发platform

任务 : 基于platform驱动模型完成LED驱动的编写&#xff0c;实现三盏灯的点亮 驱动代码 #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mod_devicetable.h> #include <linux/of.h>…

Linux | 进程终止与进程等待

目录 前言 一、进程终止 1、进程终止的几种可能 2、exit 与 _exit 二、进程等待 1、为什么要进程等待 2、如何进行进程等待 &#xff08;1&#xff09;wait函数 &#xff08;2&#xff09;waitpid函数 3、再次深刻理解进程等待 前言 我们前面介绍进程时说子进程退出…

pytorch复现4_Resnet

ResNet在《Deep Residual Learning for Image Recognition》论文中提出&#xff0c;是在CVPR 2016发表的一种影响深远的网络模型&#xff0c;由何凯明大神团队提出来&#xff0c;在ImageNet的分类比赛上将网络深度直接提高到了152层&#xff0c;前一年夺冠的VGG只有19层。Image…

uniapp 关于 video 组件的缩放比例问题

在 container 样式的 padding-bottom 设置比例值 9/16 比例值&#xff1a;56.25% 3/4 比例值&#xff1a;75% <view class"container"><video class"video-box" src"xxx.mp4" /> </view> .container {position: relative;wid…

Redis(01)| 数据结构

这里写自定义目录标题 Redis 速度快的原因除了它是内存数据库&#xff0c;使得所有的操作都在内存上进行之外&#xff0c;还有一个重要因素&#xff0c;它实现的数据结构&#xff0c;使得我们对数据进行增删查改操作时&#xff0c;Redis 能高效的处理。 因此&#xff0c;这次我…

作为20年老程序员,我如何使用GPT4来帮我写代码

如果你还在用google寻找解决代码bug的方案&#xff0c;那你真的out了&#xff0c;试试gpt4, save my life. 不是小编危言耸听&#xff0c;最近用gpt4来写代码极大地提高了代码生产力和运行效率&#xff0c;今天特地跟大家分享一下。 https://www.promptspower.comhttps://www.…

element ui dialog嵌套的页面组件,第二次点击的时候,dialog里面的嵌套的页面组件不加载数据问题

之前的代码 <el-dialog :title"title" :visible.sync"open" width"850px" append-to-body :close-on-click-modal"false" >优化之后的代码 <el-dialog :title"title" :visible.sync"open" v-if"op…

测开 (Junit 单元测试框架)

目录 了解 Junit 引入相关依赖 1、Junit注解 Test BeforeEach、BeforeAll AfterEach && AfterAll 2、断言 1、Assertions - assertEquals 方法 2、Assertions - assertNotEquals 方法 3、Assertions - assertTrue && assertFalse方法 4、Assertions…

Microsoft365个人版与家庭版有哪些功能区别?

Microsoft 365个人版与家庭版均能享受完整的Microsoft 365功能与权益&#xff0c;稍有不同的是&#xff0c;Microsoft 365家庭版可供6人使用&#xff0c;而个人版是仅供一人使用。 个人版可以同时登入5台设备&#xff0c;家庭版每人也可以登入5台设备&#xff0c;每个人都可以享…

【Linux】centos安装配置及远程连接工具的使用

前言 CentOS 是什么&#xff1f; CentOS社区企业操作系统&#xff08;Community Enterprise Operating System&#xff09; CentOS 是众多 Linux 发行版中的一种。全称&#xff1a; The Community ENTerprise Operating System 。 她是将 Red Hat Enterprise Linux &#xff…

sitespeedio.io 前端页面监控安装部署接入influxdb 到grafana

1.docker部署influxdb,部署1.8一下&#xff0c;不然语法有变化后面用不了grafana模板 docker run -d -p 8086:8086 --name influxdb -v $PWD/influxdb-data:/var/lib/influxdb influxdb:1.7.11-alpine docker exec -it influxdb_id bash #influx create user admin with pass…

Yakit工具篇:WebFuzzer模块之重放和爆破

简介 Yakit的Web Fuzzer模块支持用户自定义HTTP原文发送请求。为了让用户使用简单&#xff0c;符合直觉&#xff0c;只需要关心数据相关信息&#xff0c;Yakit后端(yaklang)做了很多工作。 首先我们先来学习重放请求的操作&#xff0c;在日常工作中可以使用 Web Fuzzer进行请…