0%

《Go语言编程》笔记之音乐盒demo

本文为《Go语言编程》第三章所描述的示例实现,主要演示并总结Go语言面向对象编程特性。
Go语言的面向对象特性设计简洁,通过接口的设计,摒弃了继承、构造、析构、虚函数等臃肿的机制。
Go语言的接口并不是其他语言(C++、Java、C#等)中所提供的接口概念,在Go语言出现之前,接口主要作为不同组件之间的契约存在。对契约的实现是强制的,你必须声明你的确实现了该接口。为了实现一个接口,你需要从该接口继承所有的方法,即使另外有一个接口实现了与该接口完全一样的接口方法甚至名字也相同只不过位于不同的名字空间下,编译器也会认为上面的类只实现了该接口而没有实现定义相同的那个接口。这类接口我们称为侵入式接口。“侵入式”的主要表现在于实现类需要明确声明自己实现了某个接口。这种强制性的接口继承是面向对象编程思想发展过程中一个遭受相当多置疑的特性。
在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口。Go语言在接口上的调整有以下三个优点:
其一,Go语言的标准库,再也不需要绘制类库的继承树图。你一定见过不少C++、Java、C#类库的继承树图。在Go中,类的继承树并无意义,你只需要知道这个类实现了哪些方法,每个方法是什么含义,就足够了。
其二,实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,而不用事前规划。
其三,不用为了实现一个接口而导入一个包,因为多引用一个外部的包,就意味着更多的耦合。接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口。
了解了Go语言面向对象的特性之后,可以通过一个音乐盒的小示例来深刻感受。
demo地址:https://github.com/Vector-DY/gostudy/tree/main/musicplayer

文件结构

实际效果

音乐库管理

首先先来定义音乐信息的结构体

1
2
3
4
5
6
7
type MusicEntry struct {
Id string
Name string
Artist string
Source string
Type string
}

之后实现具体的类型方法,我们使用一个数组切片作为基础存储结构,其他
的操作都只是对这个数组切片的包装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type MusicManager struct {
musics []MusicEntry
}

func NewMusicManager() *MusicManager {
return &MusicManager{make([]MusicEntry, 0)}
}

func (m *MusicManager) Len() int {
return len(m.musics)
}

func (m *MusicManager) Get(index int) (music *MusicEntry, err error) {
if index < 0 || index >= len(m.musics) {
return nil, errors.New("Index out of range.")
}
return &m.musics[index], nil
}

基本增删查改操作

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
func (m *MusicManager) Find(name string) *MusicEntry {
if len(m.musics) == 0 {
return nil
}

for _, m := range m.musics {
if m.Name == name {
return &m
}
}

return nil
}

func (m *MusicManager) Add(music *MusicEntry) {
m.musics = append(m.musics, *music)
}

func (m *MusicManager) Remove(index int) *MusicEntry {
if index < 0 || index >= len(m.musics) {
return nil
}
removedMusic := &m.musics[index]

if index < len(m.musics)-1 {
m.musics = append(m.musics[:index-1], m.musics[index+1:]...)
} else if index == 0 {
m.musics = make([]MusicEntry, 0)
} else {
m.musics = m.musics[:index-1]
}

return removedMusic
}

func (m *MusicManager) RemoveByName(name string) *MusicEntry {
index := -1
for i, item := range m.musics {
if item.Name == name {
index = i
break
}
}
if index == -1 {
return nil
}
return m.Remove(index)
}

音乐播放模块

1
2
3
4
5
6
7
8
type Player interface {
Play(source string)
}

func Play(source, mtype string) {
var p Player
p.Play(source)
}

简单的小示例,自行编写main函数编译运行即可,重在理解音乐库管理中的面向对象特性。