
结合前面学到的知识,在本节中,我们将设计和实现音乐播放器程序. 该应用仅用于演示Go语言的功能,因此您不应期望看到华丽的播放界面并听到优美的音乐. 接下来,我们将演示以下关键过程:
由于Go最初定位为高度并发的服务器端程序,因此它并没有在GUI支持上花费很多精力,并且当前版本的Go语言标准库不提供与GUI相关的功能或成熟的第三方程序. 方接口. 库,因此不适合开发GUI程序.
因此,该程序仍然是命令行程序,我们将其命名为SimpleMediaPlayer(SMP). 程序运行后进入循环,用于监视命令输入的状态. 该程序将接受以下命令.
我们首先实现音乐库管理模块播放器 命令行,该模块可以管理音乐. 每种音乐都包含以下信息:
让我们首先定义音乐的结构,如下所示:
typeMusicstruct{IdstringNamestringArtiststringSourcestringTypestring}
然后,我们开始实现这种音乐库管理类型,其中我们将数组切片用作基本存储结构,而其他操作实际上只是打包此数组切片. 代码如下所示.
//manager.gopackagelibraryimport"errors"typeMusicManagerstruct{musics[]MusicEntry}funcNewMusicManager()*MusicManager{return&MusicManager{make([]MusicEntry,0)}}func(m*MusicManager)Len()int{returnlen(m.musics)}func(m*MusicManager)Get(indexint)(music*MusicEntry,errerror){ifindex<0||index>=len(m.musics){returnnil,errors.New("Indexoutofrange.")}return&m.musics[index],nil}func(m*MusicManager)Find(namestring)*MusicEntry{iflen(m.musics)==0{returnnil}for_,m:=rangem.musics{ifm.Name==name{return&m}}returnnil}func(m*MusicManager)Add(music*MusicEntry){m.musics=append(m.musics,*music)}func(m*MusicManager)Remove(indexint)*MusicEntry{ifindex<0||index>=len(m.musics){returnnil}removedMusic:=&m.musics[index]//从数组切片中删除元素ifindex<len(m.musics)-1{//中间元素m.musics=append(m.musics[:index-1],m.musics[index+1:])}elseifindex==0{//删除仅有的一个元素m.musics=make([]MusicEntry,0)}else{//删除的是最后一个元素m.musics=m.musics[:index-1]}returnremovedMusic}
在实现了如此重要的基本数据管理模块之后,我们应该立即编写单元测试,而不是给自己一个借口以等待将来添加它. 以下代码实现了MusicManager类型的单元测试.

//manager_test.gopackagelibraryimport("testing")funcTestOps(t*testing.T){mm:=NewMusicManager()ifmm==nil{t.Error("NewMusicManagerfailed.")}ifmm.Len()!=0{t.Error("NewMusicManagerfailed,notempty.")}m0:=&MusicEntry{"1","MyHeartWillGoOn","CelionDion",Pop,"http://qbox.me/24501234",MP3}mm.Add(m0)ifmm.Len()!=1{t.Error("MusicManager.Add()failed.")}m:=mm.Find(m0.Name)ifm==nil{t.Error("MusicManager.Find()failed.")}ifm.Id!=m0.Id||m.Artist!=m0.Artist||m.Name!=m0.Name||m.Genre!=m0.Genre||m.Source!=m0.Source||m.Type!=m0.Type{t.Error("MusicManager.Find()failed.Founditemmismatch.")}m,err:=mm.Get(0)ifm==nil{t.Error("MusicManager.Get()failed.",err)}m=mm.Remove(0)ifm==nil||mm.Len()!=0{t.Error("MusicManager.Remove()failed.",err)}}
这个单元测试似乎有点懒,但是它基本上涵盖了MusicManager的所有功能,实际上,它确实测量了MusicManager实现中的几个问题. 因此,养成良好的单元测试习惯非常有价值.
我们接下来设计并实现音乐播放模块. 根据我们之前设定的目标,音乐播放器模块应该易于扩展,并且每次添加对新音乐文件类型的支持时,您都不需要调整代码. 让我们设计一个简单但通用的播放功能:
funcPlay(source,mtypestring)
MusicEntry在这里没有直接作为参数传递,因为MusicEntry包含一些额外的信息. 按照最低原则,我们只需要传递我们真正需要的信息,即音乐文件的位置和音乐的类型.
我们在这里设计一个简单的界面:
typePlayerinterface {
播放(源字符串)
}

然后,我们可以通过一批类型(例如MP3Player和WAVPlayer等)实现此接口,该类型已实现了尽可能多的体系结构灵活性. 因此,我们可以实现以下代码中所示的total entry函数.
//play.gopackagempimport"fmt"typePlayerinterface{Play(sourcestring)}funcPlay(source,mtypestring){varpPlayerswitchmtype{case"MP3":p=&MP3Player{}case"WAV":p=&WAVPlayer{}default:fmt.Println("Unsupportedmusictype",mtype)return}p.Play(source)}
由于我们的示例并未真正实现多媒体文件的解码和播放过程,因此对于MP3Player和WAVPlayer,我们仅以其中之一作为示例. 代码如下所示.
//mp3.gopackagempimport("fmt""time")typeMP3Playerstruct{statintprogressint}func(p*MP3Player)Play(sourcestring){fmt.Println("PlayingMP3music",source)p.progress=0forp.progress<100{time.Sleep(100*time.Millisecond)//假装正在播放fmt.Print(".")p.progress+=10}fmt.Println("nFinishedplaying",source)}
当然,我们还应该对播放过程进行单元测试. 由于单元测试相对简单,因此此处不再列出完整的单元测试代码.
核心模块已经过设计和实施,现在该使用它们了. 我们的主程序是命令行交互程序,用户可以通过输入命令来控制播放过程并获取播放信息. 由于主程序与面向对象的关系不大,因此我们在此处仅列出源代码是出于完整性的考虑,而没有太多的解释.
在这里,我们可以看一下Go语言中命令行交互程序的一般实现. 以下代码实现了音乐播放器的主程序.
//mplayer.gopackagemainimport("bufio""fmt""os""strconv""strings""pkg/mplayer/mlib""pkg/mplayer/mp")varlib*library.MusicManagervaridint=1varctrl,signalchanintfunchandleLibCommands(tokens[]string){switchtokens[1]{case"list":fori:=0;i<lib.Len();i++{e,_:=lib.Get(i)fmt.Println(i+1,":",e.Name,e.Artist,e.Source,e.Type)}case"add":{iflen(tokens)==6{id++lib.Add(&library.MusicEntry{strconv.Itoa(id),tokens[2],tokens[3],tokens[4],tokens[5]})}else{fmt.Println("USAGE:libadd<name><artist><source><type>")}}case"remove":iflen(tokens)==3{lib.RemoveByName(tokens[2])}else{fmt.Println("USAGE:libremove<name>")}default:fmt.Println("Unrecognizedlibcommand:",tokens[1])}}funchandlePlayCommand(tokens[]string){iflen(tokens)!=2{fmt.Println("USAGE:play<name>")return}e:=lib.Find(tokens[1])ife==nil{fmt.Println("Themusic",tokens[1],"doesnotexist.")return}mp.Play(e.Source,e.Type,ctrl,signal)}funcmain(){fmt.Println(`Enterfollowingcommandstocontroltheplayer:liblistViewtheexistingmusicliblibadd<name><artist><source><type>Addamusictothemusicliblibremove<name>Removethespecifiedmusicfromthelibplay<name>Playthespecifiedmusic`)lib=library.NewMusicManager()r:=bufio.NewReader(os.Stdin)for{fmt.Print("Entercommand->")rawLine,_,_:=r.ReadLine()line:=string(rawLine)ifline=="q"||line=="e"{break}tokens:=strings.Split(line,"")iftokens[0]=="lib"{handleLibCommands(tokens)}elseiftokens[0]=="play"{handlePlayCommand(tokens)}else{fmt.Println("Unrecognizedcommand:",tokens[0])}}}
所有代码均已编写播放器 命令行,现在您可以开始构建和运行程序,如下所示:

$ gorunmplayer.go
输入以下命令控制播放器:
liblist –查看现有的音乐库
libadd <名称> <艺术家> <源代码> <类型> –音乐库中的附加音乐
libremove <名称> –从库中删除指定的音乐
播放<名称> –播放指定的音乐
Entercommand-> libaddHugeStoneMJ〜/ MusicLib / hs.mp3MP3
Entercommand-> playHugeStone
正在播放MP3music〜/ MusicLib / hs.mp3

……….
完成播放〜/ MusicLib / hs.mp3
Entercommand-> liblist
1: HugeStoneMJ〜/ MusicLib / hs.mp3MP3
Entercommand-> libview
Entercommand-> q
尽管已经编写了该程序,但显然与实际程序相去甚远. 让我们谈谈剩余的问题和对策.
目前,我们的程序只是一个单一的任务程序,也就是说,一个任务只能同时执行,例如,在播放音乐时,用户无法执行其他任何操作. 作为运行在现代多任务操作系统上的应用程序,这种方法对于用户绝对是不可接受的.
音乐播放过程不应导致用户界面无响应,因此播放应在单独的线程中并能够与主程序通信. 就像一般的媒体播放器一样,在播放音乐时,我们甚至需要支持某些视觉效果的回放,即至少需要很少的线程: 用户界面,音乐回放和视频回放.
考虑到这一要求,我们自然想到了一个使用Go语言的goroutine. 例如,在对上面的播放稍作修改后,可以将Play()函数作为独立的goroutine运行.
由于当前设计是单任务,因此播放过程无法接受外部输入. 但是,作为成熟的播放器,我们至少需要支持暂停和停止功能,甚至包括设置当前播放位置. 假设我们已将播放过程放入一个独立的goroutine中,那么现在是如何控制此goroutine的方法,可以使用Go语言频道功能来实现.
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/bofangqi/article-246085-1.html
为应对美国的军事威胁