声明:首先说明这个项目来自于许式伟的《Go语言编程》,书中也给出了详尽的源代码描述,不过代码中还是存在一些问题,首先说明一下所存在的问题
问题一:音乐的播放结构体中定义了五个属性字段,在后面赋值的时候又变成了六个字段的赋值
问题二:Play函数在调用的时候多传递了两个参数,在函数原型的时候只有两个参数
问题三:RemoveByName方法并没有实现
这两个问题应该都是为了后期更好的进行项目扩展,不过作为小白的我现在还是以学习和调通代码为主,再此基础上面在进行一步步的功能扩展,下面给出响应的项目说明和可以运行的实现基本功能的代码实现,所有的实现细节已经给出了详细的注释:
一:整体的逻辑框架
二:音乐库管理模块
》Len 求歌曲名长度
》Get 通过索引查找歌曲
》Find 通过名字查找歌曲
》Add 添加歌曲
》Remove 通过索引删除歌曲
》RemoveByName 通过歌名删除歌曲
package library
import (
"errors"
"fmt"
)
//定义音乐结构体
type MusicEntry struct {
Id string //音乐的唯一Id
Name string //音乐名
Artist string //艺术家名
Source string //音乐位置
Type string //音乐类型(MP3和WAV)
}
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], err
}
//通过对比名字查看要找的歌曲是否存在
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:]...)
}
return &removedMusic
}
//通过歌名删除歌曲
func (m *MusicManager) RemoveByName(name string) *MusicEntry {
removedMusic := m.Find(name)
if removedMusic == nil{
fmt.Println("Want to delete the song does not exist")
return nil
}
return removedMusic
}
二:音乐管理单元测试模块(go单元测试测试将在后面的学习过程在详细说明,现在只要知道测试的重要性即可)
package library
import "testing"
func TestOps(t *testing.T) {
mm := NewMusicManager() //新建管理测试
if mm == nil {
t.Error("NewManagerMusic faild")
}
if mm.Len() != 0 { //len测试
t.Error("NewManagerMusic faild, not empty")
}
m0 := &MusicEntry{
"1",
"My Heart Will Go On",
"Celion Dion",
//"Pop",
"http://qbox.me/24501234",
"Mp3",
}
mm.Add(m0) //添加测试
if mm.Len() != 1 {
t.Error("MusicManager.Add() failed")
}
m := mm.Find(m0.Name) //find测试
if m == nil {
t.Error("MusicManager.Find() failed")
}
if m.Id != m0.Id || m.Name != m0.Name || m.Artist != m0.Artist ||
m.Source != m0.Source || m.Type != m0.Type {
t.Error("MusicManager.Find() failed. Find item mismatch")
}
m, err := mm.Get(0) //get测试
if m == nil {
t.Error("MusicManager.Get() failed", err)
}
//空的时候直接进行测试会出问题
m = mm.Remove(0) //Remove测试
if m == nil || mm.Len() != 0 {
t.Error("MusicManager.Remove() failed", err)
}
}
三:播放实现(播放包括的格式有MP3和WAV两种格式,当然再次也可以扩张使其支持其他格式的播放)
package mp
//音乐播放模块
import "fmt"
//设计一个简单的接口避免将MusicEntry中多余的信息传入
type Player interface {
Play(Source string)
}
//播放实现,再此也可以在添加其他格式
func Play(Source, mtype string) {
var p Player
switch mtype {
case "MP3": //MP3格式播放
p = &MP3Player{}
case "WAV": //WAV格式播放
p = &WAVPlayer{}
default:
fmt.Println("Unsupported music type", mtype)
return
}
p.Play(Source)
}
package mp
import (
"fmt"
"time"
)
type MP3Player struct {
stat int
progress int
}
//MP3格式播放具体实现
func (p *MP3Player) Play(Source string) {
fmt.Println("Playing MP3 music", Source)
p.progress = 0
for p.progress < 100 {
time.Sleep(100 * time.Millisecond) //假装正在播放
fmt.Print(".")
p.progress += 10
}
fmt.Println("\nFinished playing", Source)
}
package mp
import (
"fmt"
"time"
)
type MP3Player struct {
stat int
progress int
}
//MP3格式播放具体实现
func (p *MP3Player) Play(Source string) {
fmt.Println("Playing MP3 music", Source)
p.progress = 0
for p.progress < 100 {
time.Sleep(100 * time.Millisecond) //假装正在播放
fmt.Print(".")
p.progress += 10
}
fmt.Println("\nFinished playing", Source)
}
四:主程序调用实现
package main
import (
"goCode/src/music/library"
"fmt"
"strconv"
"goCode/src/music/mp"
"bufio"
"os"
"strings"
)
var lib *library.MusicManager
var id int = 1
//管理模块句柄实现
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++
lib.Add(&library.MusicEntry{strconv.Itoa(id),
tokens[2], tokens[3], tokens[4], tokens[5]})
}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("Unrecognied lib commond:", tokens[1])
}
}
//播放模块句柄实现
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)
}
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 = library.NewMusicManager()
r := bufio.NewReader(os.Stdin)
for{
fmt.Print("Enter Command-> ")
rawLine, _, _ := r.ReadLine()
line := string(rawLine)
//输入q或者e时退出播放器
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])
}
}
}
五:演示