搜索
首页后端开发GolangGolang 中的面向对象编程 (OOP) 简介

当我们谈论编程时,我们通常指的是编写一堆修改某些数据并与某些数据交互的函数。面向对象编程(OOP)是一种编程模型,它专注于包含数据并附加一些相关功能的“对象”。面向对象编程有四大支柱:继承、封装、多态性和抽象。在本博客中,我们将通过示例了解如何在 Golang 中实现它们中的每一个。推荐一些有关 OOP 的基本概念,但如果没有,我将简要介绍所有四个支柱的含义。

Introduction to Object Oriented Programming (OOP) in Golang

类、对象和方法

面向对象编程的核心思想可以概括为以下几点:

  • 您定义“类”,它们是数据和可以调用该数据的函数的集合。
  • 这些特定的函数称为该特定类的“方法”。
  • 类的实际实例称为“对象”。

让我们看一些 Golang 中的代码来理解这三个概念:

package main

import "fmt"

type Batman struct {
    actor string
    year int
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year)
}

func main() {
    b1 := Batman{actor: "Michael Keaton", year: 1989}
    b2 := Batman{actor: "Christian Bale", year: 2005}

    b1.SayImBatman()
    b2.SayImBatman()
}

在Golang中,类只不过是我们定义的类型。这些类型不一定必须是结构体,但通常都是结构体,因为在 OOP 中我们使用数据集合,这些数据可以是任何类型(字符串、整数等)。

类是对象的蓝图。每当实例化一个类时,就会形成一个对象。在此示例中,b1 和 b2 是 Batman 类的对象。

SayImBatman 函数可以在该类的任何对象上调用。由于它与 Batman 类相关,因此不将其称为常规函数,而是将其称为类的方法

我认为这应该足以让您清楚 OOP 的基础知识,以便您继续进行下一部分,我们将在其中介绍 OOP 的四大支柱。

遗产

继承引入了 OOP 中 parentchild 类的概念。子类是从父类派生的类,并继承其所有方法和属性(数据)。让我们看一些代码来帮助我们理解这一点:

package main

import "fmt"

type Hero struct {
    team string
}

type Batman struct {
    Hero
    name string
}

type Ironman struct {
    Hero
    power int
}

func (h Hero) SayTeam() {
    fmt.Println("My Team is", h.team)
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman\n", b.name)
}

func (i Ironman) SayPowerLevel() {
    fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power)
}

func main() {
    b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"}
    i1 := Ironman{Hero{team: "Avengers"}, 23}

    b1.SayImBatman()
    b1.SayTeam()

    i1.SayPowerLevel()
    i1.SayTeam()
}

在此示例中,蝙蝠侠和钢铁侠是 Hero 父类的子类。他们可以访问其父类的属性(即 team)及其方法(即 SayTeam)。正如您在声明 b1 和 i1 实例时所看到的那样,我们指定了父类属性以及它们各自类的特定属性。它们都能够调用父类中定义的 SayTeam 方法。但它们也有各自独特的属性和方法。

Golang 使用组合(在结构体中使用结构体)来实现继承。它不像其他 OOP 语言(例如 C 或 Java)那样具有内置的基于类的继承。

封装

封装是隐藏对象内部属性并且不允许直接修改它们的原理。相反,它依赖于提供获取和更新这些属性的方法。让我们看一个例子来更好地理解这一点:

package main

import "fmt"

type Batman struct {
    actor string
    year int
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year)
}

func main() {
    b1 := Batman{actor: "Michael Keaton", year: 1989}
    b2 := Batman{actor: "Christian Bale", year: 2005}

    b1.SayImBatman()
    b2.SayImBatman()
}

package main

import "fmt"

type Hero struct {
    team string
}

type Batman struct {
    Hero
    name string
}

type Ironman struct {
    Hero
    power int
}

func (h Hero) SayTeam() {
    fmt.Println("My Team is", h.team)
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman\n", b.name)
}

func (i Ironman) SayPowerLevel() {
    fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power)
}

func main() {
    b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"}
    i1 := Ironman{Hero{team: "Avengers"}, 23}

    b1.SayImBatman()
    b1.SayTeam()

    i1.SayPowerLevel()
    i1.SayTeam()
}

在Golang中,从包中导出的属性和方法以大写字母开头。当我们在utils包中定义小写的actor和year时,我们确保它们不能被直接修改。相反,就像您在 main.go 文件中看到的那样,您需要使用导出的方法(以大写字母开头) - GetActor、SetActor 等来获取和修改它们。

这就是封装的全部内容 - 确保防止数据意外更改,并提供与数据安全交互的方法。

您会注意到不同的一件事是,在 Batman 类的所有方法中,我们使用指针接收器 *Batman 而不是像前面示例中那样使用值接收器 Batman。这是因为我们希望能够在 Set 方法中修改原始结构。在 Golang 中,最佳实践是,如果某些方法需要指针接收器,则让所有方法都使用指针接收器以保持一致性。这就是为什么 Get 方法也使用指针接收器,即使它们没有修改原始结构。

此外,还要注意的一件事是,仅仅因为我们使用的是指针接收器,我们就不必这样做:(&b1).GetActor。在 Golang 中,带有指针参数的函数必须采用指针,但带有指针接收器的方法可以采用值或指针作为接收器。

TL;DR:Golang 自动将 b1.GetActor 转换为 (&b1).GetActor,因为 GetActor 方法有一个指针接收器,但如果 GetActor 是一个普通函数,它不会将 GetActor(b1) 转换为 GetActor(&b1)一个指针参数。

多态性和抽象

OOP 的接下来两个支柱可以合并在一起,因为它们的代码示例看起来非常相似。多态性是指两个不同类的两个不同对象可以被视为同一公共超类的对象的编程实践。这意味着您可以在两个不同的对象上调用相同的函数,就好像它们是同一类的对象一样。这应该开始让您感受到所涉及的接口的味道:)

让我们看一些代码来更好地理解这一点:

package main

import "fmt"

type Batman struct {
    actor string
    year int
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year)
}

func main() {
    b1 := Batman{actor: "Michael Keaton", year: 1989}
    b2 := Batman{actor: "Christian Bale", year: 2005}

    b1.SayImBatman()
    b2.SayImBatman()
}

在此示例中,StartFight 函数可以传递 b1 和 i1 对象,即使它们彼此没有任何关系。尝试理解这与继承有何不同,在继承中,子类可以访问父类的方法。在此示例中,没有子类和父类(也没有共享方法)。相反,两个不同的对象被函数视为相同:这称为多态性。

现在,这也可以被视为抽象的一个例子。顾名思义,抽象是一种隐藏实现细节,而只是提供为您处理事务的函数的编程实践。在这个例子中,你不需要关心单个英雄的方法是如何配置的。当您想使用任何英雄的战斗功能时,您可以随时继续使用开始战斗功能。这样,实现细节对用户来说是隐藏的,只暴露基本的细节。

现在回到多态,有两个更常见的例子,那就是方法重写和重载。

方法重写

方法重写是指子类定义自己的父类中定义的方法的实现。现在使用此实现来代替原始父类的实现。让我们看看之前用于继承的代码,看看它在方法重写后的样子:

package main

import "fmt"

type Hero struct {
    team string
}

type Batman struct {
    Hero
    name string
}

type Ironman struct {
    Hero
    power int
}

func (h Hero) SayTeam() {
    fmt.Println("My Team is", h.team)
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman\n", b.name)
}

func (i Ironman) SayPowerLevel() {
    fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power)
}

func main() {
    b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"}
    i1 := Ironman{Hero{team: "Avengers"}, 23}

    b1.SayImBatman()
    b1.SayTeam()

    i1.SayPowerLevel()
    i1.SayTeam()
}

该程序的输出是:

//oops-in-go/utils/utils.go

package utils

type Batman struct {
    actor string
    year int
}

func (b *Batman) GetActor() string {
    return b.actor
}

func (b *Batman) GetYear() int {
    return b.year
}

func (b *Batman) SetActor(actor string) {
    b.actor = actor
}

func (b *Batman) SetYear(year int) {
    b.year = year
}

Batman 类的对象现在使用自己的 SayTeam 方法,而不是父 Hero 类的方法。由于 Ironman 类没有自己的 SayTeam 方法,因此它的对象仍然使用其父类的方法。这就是方法重写的意思,子类“重写”父类中定义的方法。

方法重载

这是指同一个函数能够接受多个不同的参数。这些参数的数量或类型可能不同。 Golang 提供了两种方法来实现这一点:通过可变参数函数,另一种通过接口。

让我们看一下两者的代码,这将帮助您更好地理解:

使用可变参数函数

// oops-in-go/main.go

package main

import (
    "fmt"
    "oops-in-go/utils"
)

func main() {
    b1 := utils.Batman{}
    b1.SetActor("Michael Keaton")
    b1.SetYear(1989)
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b1.GetActor(), b1.GetYear())

    b1.SetActor("Christian Bale")
    b1.SetYear(2005)
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b1.GetActor(), b1.GetYear())
}

在这里,您可以使用任意数量个参数“重载”listMembers函数。

使用接口

package main

import "fmt"

type Hero interface {
    Fight()
}

type Batman struct {
    weapon string
}

type Ironman struct {
    weapon string
}

func (b Batman) Fight() {
    fmt.Printf("Batman hits with a %s\n", b.weapon)
}

func (i Ironman) Fight() {
    fmt.Printf("Ironman hits with a %s\n", i.weapon)
}

func StartFight(h Hero) {
    fmt.Println("Fight has started.")
    h.Fight()
}

func main() {
    b1 := Batman{"Batarang"}
    i1 := Ironman{"Repulsor rays"}

    StartFight(b1)
    StartFight(i1)
}

该程序的输出是:

package main

import "fmt"

type Hero struct {
    team string
}

type Batman struct {
    Hero
    name string
}

type Ironman struct {
    Hero
    power int
}

func (h Hero) SayTeam() {
    fmt.Println("My Team is", h.team)
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman\n", b.name)
}

func (i Ironman) SayPowerLevel() {
    fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power)
}

func (b Batman) SayTeam() {
    fmt.Printf("I'm Batman and my team is %s\n", b.team)
}

func main() {
    b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"}
    i1 := Ironman{Hero{team: "Avengers"}, 23}

    b1.SayImBatman()
    b1.SayTeam()

    i1.SayPowerLevel()
    i1.SayTeam()
}

这里我们“重载”了 saySomething 方法来接受不同类型的参数。我们采用一个空接口作为参数,它可以是任何类型,然后使用 switch case 检查其类型并相应地打印输出。

结论

我很清楚这是一篇很长的文章,如果您坚持读到最后,我想让您知道我真的很高兴:) 我真诚地希望您学到很多有关面向对象编程的新知识以及如何在 Golang 中实现它。我在我的网站上撰写有关不同技术概念的博客,如果您有兴趣学习新事物,我建议您订阅我的时事通讯。

以上是Golang 中的面向对象编程 (OOP) 简介的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
如何使用'字符串”软件包逐步操纵字符串如何使用'字符串”软件包逐步操纵字符串May 13, 2025 am 12:12 AM

Go的strings包提供了多种字符串操作功能。1)使用strings.Contains检查子字符串。2)用strings.Split将字符串分割成子字符串切片。3)通过strings.Join合并字符串。4)用strings.TrimSpace或strings.Trim去除字符串首尾的空白或指定字符。5)用strings.ReplaceAll替换所有指定子字符串。6)使用strings.HasPrefix或strings.HasSuffix检查字符串的前缀或后缀。

Go Strings软件包:如何改进我的代码?Go Strings软件包:如何改进我的代码?May 13, 2025 am 12:10 AM

使用Go语言的strings包可以提升代码质量。1)使用strings.Join()优雅地连接字符串数组,避免性能开销。2)结合strings.Split()和strings.Contains()处理文本,注意大小写敏感问题。3)避免滥用strings.Replace(),考虑使用正则表达式进行大量替换。4)使用strings.Builder提高频繁拼接字符串的性能。

GO BYTES软件包中最有用的功能是什么?GO BYTES软件包中最有用的功能是什么?May 13, 2025 am 12:09 AM

Go的bytes包提供了多种实用的函数来处理字节切片。1.bytes.Contains用于检查字节切片是否包含特定序列。2.bytes.Split用于将字节切片分割成smallerpieces。3.bytes.Join用于将多个字节切片连接成一个。4.bytes.TrimSpace用于去除字节切片的前后空白。5.bytes.Equal用于比较两个字节切片是否相等。6.bytes.Index用于查找子切片在largerslice中的起始索引。

使用GO的'编码/二进制”软件包掌握二进制数据处理:综合指南使用GO的'编码/二进制”软件包掌握二进制数据处理:综合指南May 13, 2025 am 12:07 AM

theEncoding/binarypackageingoisesenebecapeitProvidesAstandArdArdArdArdArdArdArdArdAndWriteBinaryData,确保Cross-cross-platformCompatibilitiational and handhandlingdifferentendenness.itoffersfunctionslikeread,写下,写,dearte,readuvarint,andwriteuvarint,andWriteuvarIntforPreciseControloverBinary

转到'字节”软件包快速参考转到'字节”软件包快速参考May 13, 2025 am 12:03 AM

回顾bytespackageingoiscialforhandlingbyteslicesandbuffers,offeringToolsforefficeMemoryManagement和datamAnipulation.1)ItProvidesfunctionalitiesLikeCreatingBuffers,比较,搜索/更换/更换/更换forlargedAtatAsetsets.n

掌握GO弦:深入研究'字符串”包装掌握GO弦:深入研究'字符串”包装May 12, 2025 am 12:05 AM

你应该关心Go语言中的"strings"包,因为它提供了处理文本数据的工具,从基本的字符串拼接到高级的正则表达式匹配。1)"strings"包提供了高效的字符串操作,如Join函数用于拼接字符串,避免性能问题。2)它包含高级功能,如ContainsAny函数,用于检查字符串是否包含特定字符集。3)Replace函数用于替换字符串中的子串,需注意替换顺序和大小写敏感性。4)Split函数可以根据分隔符拆分字符串,常用于正则表达式处理。5)使用时需考虑性能,如

GO中的'编码/二进制”软件包:您的二进制操作首选GO中的'编码/二进制”软件包:您的二进制操作首选May 12, 2025 am 12:03 AM

“编码/二进制”软件包interingoisentialForHandlingBinaryData,oferingToolSforreDingingAndWritingBinaryDataEfficely.1)Itsupportsbothlittle-endianandBig-endianBig-endianbyteorders,CompialforOss-System-System-System-compatibility.2)

Go Byte Slice操纵教程:掌握'字节”软件包Go Byte Slice操纵教程:掌握'字节”软件包May 12, 2025 am 12:02 AM

掌握Go语言中的bytes包有助于提高代码的效率和优雅性。1)bytes包对于解析二进制数据、处理网络协议和内存管理至关重要。2)使用bytes.Buffer可以逐步构建字节切片。3)bytes包提供了搜索、替换和分割字节切片的功能。4)bytes.Reader类型适用于从字节切片读取数据,特别是在I/O操作中。5)bytes包与Go的垃圾回收器协同工作,提高了大数据处理的效率。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),