案例目的
演示Go语言面向对象编程特性,设计音乐播放器程序,此程序仅仅只是演示面向对象特性。关键流程:
(1) 音乐库功能:使用者可以查看、添加和删除里面的音乐曲目;
(2)、播放音乐;
(3)、支持MP3和WAV,但也能随时扩展支持更多的音乐类型;
(4)、退出程序。
程序主要接受以下命令
(1)、音乐库管理命令:lib,包括list/add/remove命令
(2)、播放管理:play命令,play后带歌曲名字
(3)、退户程序:q命令
案例代码
(1)、音乐库管理模块,管理对象为音乐。创建package mlib(实现entry.go manager.go manager_test.go),实现对音乐文件的管理。
entry.go
package mlib
/*
**** 管理对象为音乐,定义音乐结构体类型
****
**** 音乐结构体:id,name,artist,source,type
**** 音乐id、音乐名、艺术家名、音乐位置、类型
*/
type MusicEntry struct {
Id string
Name string
Artist string
Source string
Type string
}
manager.go
package mlib
import (
"errors"
)
type MusicManager struct {
musics []MusicEntry
}
//创建Music管理对象
func NewMusicManager() *MusicManager {
a := make([]MusicEntry, 0)
return &MusicManager{
musics : a,
}
}
//长度
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
}
func (m *MusicManager) Find(name string) *MusicEntry {
if len(m.musics) == 0 || name == "" {
return nil
}
//遍历查找
for _, v := range m.musics {
if v.Name == name {
return &v
}
}
//没找到
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 > 0 && 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 {
if len(m.musics) == 0 {
return nil
}
for i, v := range m.musics {
if v.Name == name {
return m.Remove(i)
}
}
return nil
}
manager_test.go
package mlib
import (
"testing"
)
func TestOps(t *testing.T) {
mm := NewMusicManager()
if mm == nil {
t.Error("NewMusicManager failed.")
}
if mm.Len() != 0 {
t.Error("NewMusicManager failed, not empty.")
}
m0 := &MusicEntry{
Id : "1",
Name : "My Heart Will Go On",
Artist : "Celion Dion",
Source : "http://qbox.me/24501234",
Type : "MP3",
}
mm.Add(m0)
if mm.Len() != 1 {
t.Error("MusicManager.Add() failed.")
}
m := mm.Find(m0.Name)
if m == nil {
t.Error("MusicManager.Find() failed.")
}
if m.Id != m0.Id || m.Artist != m0.Artist ||
m.Name != m0.Name || m.Source != m0.Source || m.Type != m0.Type {
t.Error("MusicManager.Find() failed. Found item mismatch.")
}
m, err := mm.Get(0)
if m == nil {
t.Error("MusicManager.Get() failed.", err)
}
m = mm.Remove(0)
if m == nil || mm.Len() != 0 {
t.Error("MusicManager.Remove() failed.", err)
}
}
(2)、音乐播放模块(package mp:play.go、mp3.go、wav.go)
play.go
package mp
import (
"fmt"
)
//音乐播放模块 接口
type Player interface {
Play(source string)
}
//音乐位置及类型
func Play(source, mtype string) {
var p Player
switch mtype {
case "MP3":
p = &MP3Player{}
case "WAV":
p = &WAVPlayer{}
default:
fmt.Println("unsupported music type", mtype)
return
}
p.Play(source)
}
mp3.go
package mp
import (
"fmt"
"time"
)
type MP3Player struct{
stat int
progress int
}
func (p *MP3Player) Play(source string) {
fmt.Println("Playing MP3 music ", source)
p.progress = 0
for p.progress < 100 {
time.Sleep(time.Millisecond * 100) //延时假装正在播放
fmt.Print(".")
p.progress += 10
}
fmt.Println("\nFinished playing", source)
}
wav.go
package mp
import (
"fmt"
"time"
)
type WAVPlayer struct {
stat int
progress int
signal chan int
}
func (p *WAVPlayer) Play(source string) {
fmt.Println("Playing wav music", source)
p.progress = 0
for p.progress < 100 {
time.Sleep(100 * time.Millisecond) // 假装正在播放
fmt.Print(".")
p.progress += 10
}
fmt.Println()
}
(3)、主程序。(package main:mplayer.go)
mplayer.go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"mplayer/mlib"
"mplayer/mp"
)
//音乐管理模块 全局变量声明与赋值
var lib *mlib.MusicManager = mlib.NewMusicManager()
var id int = 1
var ctrl, signal chan int
//功能函数
func handleLibCommands(tokens []string) {
switch tokens[1] {
case "list":
for i := 0; i < lib.Len(); i++ {
e, _ := lib.Get(i)
fmt.Println(i+1, ":", e.Name, e.Artist, e.Source, e.Type)
}
case "add":
if len(tokens) == 6 {
id++
m := &mlib.MusicEntry {
strconv.Itoa(id),
tokens[2],
tokens[3],
tokens[4],
tokens[5],
}
lib.Add(m)
} else {
fmt.Println("USAGE: lib add <name> <artist> <source> <type>")
}
case "remove":
if len(tokens) == 3 {
lib.RemoveByName(tokens[2])
} else {
fmt.Println("USAGE: lib remove <name>")
}
default:
fmt.Println("Unrecognized lib command:")
}
}
func handlePlayCommand(tokens []string) {
if len(tokens) != 2 {
fmt.Println("USAGE: play <name>")
return
}
e := lib.Find(tokens[1])
if e == nil {
fmt.Println("The music", tokens[1], "does not exist.")
return
}
mp.Play(e.Source, e.Type)
}
//由 main 函数作为程序入口点启动
func main() {
fmt.Println(`
Enter following commands to control the player:
lib list -- View the existing music lib
lib add <name> <artist> <source> <type> -- Add a music to the music lib
lib remove <name> -- Remove the specified music from the lib
play <name> -- Play the specified music
`)
//lib := mlib.NewMusicManager()
r := bufio.NewReader(os.Stdin)
for {
fmt.Print("Enter command-> ")
rawLine, _, _ := r.ReadLine()
line := string(rawLine)
if line == "q" || line == "e" {
break
}
tokens := strings.Split(line, " ")
if tokens[0] == "lib" {
handleLibCommands(tokens)
} else if tokens[0] == "play" {
handlePlayCommand(tokens)
} else {
fmt.Println("Unrecognized command:", tokens[0])
}
}
}
程序运行结果:
遗留问题
(1)、多任务 : 单任务程序,至少需要这么几个线程:用户界面、音乐播放和视频播放。
(2)、控制播放
知识盲点
1、Go语言项目构建以及包的组织结构 环境变量和包组织目录结构
GoROOT环境变量是Go语言的类库,编译器回去查找标准库函数。GOPATH环境变量,构建Go环境,src文件夹下面创建project,project下面可以创建各种自建package以及源文件。
2、标准库包:errors、log、strconv、bufio、os、strings
3、构建测试程序及运行
参考书籍:《Go语言编程》许式伟