Go 面向对象编程

 

 

Go 并不是完全面向对象的编程语言。Go 官网的 FAQ 回答了 Go 是否是面向对象语言,摘录如下。

可以说是,也可以说不是。虽然 Go 有类型和方法,支持面向对象的编程风格,但却没有类型的层次结构。Go 中的“接口”概念提供了一种不同的方法,我们认为它易于使用,也更为普遍。Go 也可以将结构体嵌套使用,这与子类化(Subclassing)类似,但并不完全相同。此外,Go 提供的特性比 C++ 或 Java 更为通用:子类可以由任何类型的数据来定义,甚至是内建类型(如简单的“未装箱的”整型)。这在结构体(类)中没有受到限制。

 

结构体替代类

Go 不支持类,而是提供了结构体。结构体中可以添加属性和方法。这样可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。

文件结构:

workspacepath -> oop -> employee -> employee.go  

workspacepath -> oop -> main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//employee.go文件<br>package employee
 
import
    "fmt"
)
type Employee struct
    FirstName   string
    LastName    string
    TotalLeaves int
    LeavesTaken int
}
func (e Employee) LeavesRemaining() { 
    fmt.Printf("%s %s has %d leaves remaining", e.FirstName, e.LastName, (e.TotalLeaves - e.LeavesTaken))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//main.go文件<br>package main
 
import "oop/employee"
 
func main() { 
    e := employee.Employee {
        FirstName: "Sam",
        LastName: "Adolf",
        TotalLeaves: 30,
        LeavesTaken: 20,
    }
    e.LeavesRemaining()
}

1.Go 并不支持构造器。如果某类型的零值不可用,需要程序员来隐藏该类型,避免从其他包直接访问。程序员应该提供一种名为 NewT(parameters) 的 函数,按照要求来初始化 T 类型的变量。  

2.应该让 Employee 结构体不可引用,修改 Employee为employee,这样别的文件就不能引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//employee.go文件<br>package employee
 
import
    "fmt"
)
 
type employee struct
    firstName   string
    lastName    string
    totalLeaves int
    leavesTaken 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", e.firstName, e.lastName, (e.totalLeaves - e.leavesTaken))
}
1
2
3
4
5
6
7
8
//main.go文件<br>package main 
 
import "oop/employee"
 
func main() { 
    e := employee.New("Sam", "Adolf", 30, 20)
    e.LeavesRemaining()
}  

总结:Go 不支持类,但结构体能够很好地取代类,而以 New(parameters) 签名的方法可以替代构造器。

 

组合取代继承

Go 不支持继承,但它支持组合(Composition)。组合一般定义为“合并在一起”。汽车就是一个关于组合的例子:一辆汽车由车轮、引擎和其他各种部件组合在一起。

在 Go 中,通过在结构体内嵌套结构体,可以实现组合。

组合的典型例子就是博客帖子。每一个博客的帖子都有标题、内容和作者信息。使用组合可以很好地表示它们。通过学习本教程后面的内容,我们会知道如何实现组合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main
 
import
    "fmt"
)
 
type author struct
    firstName string
    lastName  string
    bio       string
}
 
func (a author) fullName() string { 
    return fmt.Sprintf("%s %s", a.firstName, a.lastName)
}
 
type post struct
    title   string
    content string
    author
}
 
func (p post) details() { 
    fmt.Println("Title: ", p.title)
    fmt.Println("Content: ", p.content)
    fmt.Println("Author: ", p.fullName())
    fmt.Println("Bio: ", p.bio)
}
 
func main() { 
    author1 := author{       
        "Naveen",       
        "Ramanathan",       
        "Golang Enthusiast",
    }
    post1 := post{       
        "Inheritance in Go",       
        "Go supports composition instead of inheritance",
        author1,
    }
    post1.details()
}
1
2
3
4
Title:  Inheritance in Go
Content:  Go supports composition instead of inheritance
Author:  Naveen Ramanathan
Bio:  Golang Enthusiast

  

结构体切片的嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package main
 
import
    "fmt"
)
 
type author struct
    firstName string
    lastName  string
    bio       string
}
 
func (a author) fullName() string { 
    return fmt.Sprintf("%s %s", a.firstName, a.lastName)
}
 
type post struct
    title   string
    content string
    author
}
 
func (p post) details() { 
    fmt.Println("Title: ", p.title)
    fmt.Println("Content: ", p.content)
    fmt.Println("Author: ", p.fullName())
    fmt.Println("Bio: ", p.bio)
}
 
type website struct
     posts []post
}
 
func (w website) contents() { 
    fmt.Println("Contents of Website\n")   
    for _, v := range w.posts {
        v.details()
        fmt.Println()
    }
}
     
func main() { 
    author1 := author{       
        "Naveen",       
        "Ramanathan",       
        "Golang Enthusiast",
    }
    post1 := post{       
        "Inheritance in Go",       
        "Go supports composition instead of inheritance",
        author1,
    }
    post2 := post{       
        "Struct instead of Classes in Go",       
        "Go does not support classes but methods can be added to structs",
        author1,
    }
    post3 := post{       
        "Concurrency",       
        "Go is a concurrent language and not a parallel one",
        author1,
    }
    w := website{
        posts: []post{post1, post2, post3},
    }
    w.contents()
}

在上面的主函数中,我们创建了一个作者 author1,以及三个帖子 post1post2和 post3。我们最后通过嵌套三个帖子,在第 62 行创建了网站 w,并在下一行显示内容。

程序会输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Contents of Website
 
Title:  Inheritance in Go 
Content:  Go supports composition instead of inheritance 
Author:  Naveen Ramanathan 
Bio:  Golang Enthusiast
 
Title:  Struct instead of Classes in Go 
Content:  Go does not support classes but methods can be added to structs 
Author:  Naveen Ramanathan 
Bio:  Golang Enthusiast
 
Title:  Concurrency 
Content:  Go is a concurrent language and not a parallel one 
Author:  Naveen Ramanathan 
Bio:  Golang Enthusiast

  

使用接口实现多态

Go 通过接口来实现多态。在 Go 语言中,我们是隐式地实现接口。一个类型如果定义了接口所声明的全部方法,那它就实现了该接口。

1、多个类型可以实现同一个接口。 
2、一个类型可以实现多个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main
 
import (
    "fmt"
)
 
type Income interface {
    calculate() int
    source() string
}
 
type FixedBilling struct {
    projectName string
    biddedAmount int
}
 
type TimeAndMaterial struct {
    projectName string
    noOfHours  int
    hourlyRate int
}
 
func (fb FixedBilling) calculate() int {
    return fb.biddedAmount
}
 
func (fb FixedBilling) source() string {
    return fb.projectName
}
 
func (tm TimeAndMaterial) calculate() int {
    return tm.noOfHours * tm.hourlyRate
}
 
func (tm TimeAndMaterial) source() string {
    return tm.projectName
}
 
func calculateNetIncome(ic []Income) {
    var netincome int = 0
    for _, income := range ic {
        fmt.Printf("Income From %s = $%d\n", income.source(), income.calculate())
        netincome += income.calculate()
    }
    fmt.Printf("Net income of organisation = $%d", netincome)
}
 
func main() {
    project1 := FixedBilling{projectName: "Project 1", biddedAmount: 5000}
    project2 := FixedBilling{projectName: "Project 2", biddedAmount: 10000}
    project3 := TimeAndMaterial{projectName: "Project 3", noOfHours: 160, hourlyRate: 25}
    incomeStreams := []Income{project1, project2, project3}
    calculateNetIncome(incomeStreams)
}

在上面的 main 函数中,我们创建了三个项目,有两个是 FixedBilling 类型,一个是 TimeAndMaterial 类型。接着我们创建了一个 Income 类型的切片,存放了这三个项目。由于这三个项目都实现了 Interface 接口,因此可以把这三个项目放入 Income 切片。最后我们将该切片作为参数,调用了 calculateNetIncome 函数,显示了项目不同的收益和收入来源。  

1
2
3
4
Income From Project 1 = $5000
Income From Project 2 = $10000
Income From Project 3 = $4000
Net income of organisation = $19000 

 

refer:https://studygolang.com/articles/12681

posted @   -零  阅读(438)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
历史上的今天:
2018-11-06 Web轻量级扫描工具Skipfish
点击右上角即可分享
微信分享提示