请选择 进入手机版 | 继续访问电脑版
开启辅助访问
链路首页链路财经目前收录 币种 : 4908 交易所 : 310钱包 : 17 24H 交易量 : $43,403,137,051 总市值 : $245,388,183,835
2019
09/11
12:41
分享
评论
  • 很多linux的开发者在研究shell的时候,都会发现有一个示例是用shell编写一个音乐播放器。作为Go语言的学习者,我们同样可以实现这样一个音乐播放器。这要用到Go语言的面向对象编程与接口编程,接下来我们逐步实现它!

     

    首先,我们先定义一个音乐类数据的结构体,它可以包含如下信息:

    type MusicEntry struct {  Id     string //编号  Name   string //歌名  Artist string //作者  Source string //位置  Type   string //类型}

    我们来定义一个音乐库,也就是把上述内容形成一个集合,Go语言的集合可以很方便的达成要求。

    type MusicManager struct {  musics []MusicEntry}

    听过我们直播课程的小伙伴一定知道,在定义结构体后,一般情况下都会提供一个入口函数,便于操作。

    //入口函数func NewMusicManager() *MusicManager {  return &MusicManager{make([]MusicEntry, 0)}}

    接下来一口气实现3个功能函数:求曲库数量,根据编号获得歌曲,根据名称查找歌曲,添加歌曲

    //返回曲库当前数量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) (me *MusicEntry, index int) { if len(m.musics) == 0 { return nil, -1 } for k, m := range m.musics { if m.Name == name { return &m, k } } return nil, -1}
    //像歌曲库添加一首歌曲func (m *MusicManager) Add(music *MusicEntry) { m.musics = append(m.musics, *music)}

    曲库当然也可以删除歌曲,我们提供2个接口:按编号或者按名称

    //按编号删除歌曲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) (me *MusicEntry, index int) { e, idx := m.Find(name) if e == nil { return nil, idx } m.Remove(idx) return e, idx}

    完整的manager.go的代码如下:

    /*   author:Yekai   companydj   filename:manager.go*/package main
    import "errors"
    type MusicEntry struct { Id string //编号 Name string //歌名 Artist string //作者 Source string //位置 Type string //类型}
    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}
    //查询歌曲信息func (m *MusicManager) Find(name string) (me *MusicEntry, index int) { if len(m.musics) == 0 { return nil, -1 } for k, m := range m.musics { if m.Name == name { return &m, k } } return nil, -1}
    //像歌曲库添加一首歌曲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) (me *MusicEntry, index int) { e, idx := m.Find(name) if e == nil { return nil, idx } m.Remove(idx) return e, idx}

    接下来我们考虑实现play这个动作,为了支持扩展,支持多种类型,我们定义一个接口,对于不同的歌曲类型,都支持此接口就行了。

    type Player interface {  Play(source string)}

    我们先来实现一个mp3的播放器,只需要实现play接口即可!

    //mp3 播放器type MP3Player struct {  stat     int //状态  progress int //进度}
    func (p *MP3Player) Play(source string) { fmt.Println("Playing MP3 music", source) for p.progress < 100 { time.Sleep(100 * time.Millisecond) // 这一定是一个假的播放器,只有进度,没有声音 fmt.Print(".") p.progress += 10 } fmt.Println("\nFinished playing", source)}

    当然这里的播放并非去调用解码器、硬件驱动等等,我们只是一个模拟播放。

     

    同样的,我们可以再支持wav类型的歌曲播放,套路相同。

    /*   author:Yekai   companydj   filename:wav.go*/package main
    import ( "fmt" "time")
    //wav 播放器type WavPlayer struct { stat int //状态 progress int //进度}
    //播放音乐,显示进度func (p *WavPlayer) Play(source string) { fmt.Println("Playing wav music", source) for p.progress < 100 { time.Sleep(100 * time.Millisecond) // 这一定是一个假的播放器,只有进度,没有声音 fmt.Print(".") p.progress += 10 } fmt.Println("\nFinished playing", source)}

    我们将播放的方法统一封装:

    /*   author:Yekai   companydj   filename:play.go*/package main
    import "fmt"
    type Player interface { Play(source string)}
    //播放只需要知道类型和数据位置就可以了func Play(source, mtype string) { var p Player switch mtype { case "MP3": p = &MP3Player{0, 0} case "WAV": p = &WavPlayer{0, 0} default: fmt.Println("Unsupported music type", mtype) return } p.Play(source)}

    职能函数都写完了,剩下的就是写主控逻辑了,我们编写一个接收输入的命令行窗口,给用户设计一组可以操作的指令来添加歌曲、删除歌曲、播放歌曲等,当然退出也是要给提供的。

    func main() {  fmt.Println(`        Enter following commands to control the player:        lib list -- View the existing music lib        lib add  -- Add a music to the music lib        lib remove  -- Remove the specified music from the lib        play  -- Play the specified music    `)    //曲库的入口  lib = NewMusicManager()  r := bufio.NewReader(os.Stdin)  for {    fmt.Print("Enter command-> ")    rawLine, _, _ := r.ReadLine()    line := string(rawLine)    if line == "quit" || line == "QUIT" {      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])    }  }}

    很显然,接下来我们实现handleLibCommandshandlePlayCommand就可以了!

    //根据歌曲库的处理如下: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(&MusicEntry{strconv.Itoa(id),          tokens[2], tokens[3], tokens[4], tokens[5]})      } else {        fmt.Println("USAGE: lib add ")      }    }  case "remove":    if len(tokens) == 3 {      lib.RemoveByName(tokens[2])    } else {      fmt.Println("USAGE: lib remove ")    }  default:    fmt.Println("Unrecognized lib command:", tokens[1])  }}

    如果用户想要播放歌曲,就是实现handlePlayCommand

    func handlePlayCommand(tokens []string) {  if len(tokens) != 2 {    fmt.Println("USAGE: play ")    return  }  e, _ := lib.Find(tokens[1])  if e == nil {    fmt.Println("The music", tokens[1], "does not exist.")    return  }  Play(e.Source, e.Type)}

    完整的main.go如下:

    /*   author:Yekai   companydj   filename:main.go*/package main
    import ( "bufio" "fmt" "os" "strconv" "strings")
    var lib *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(&MusicEntry{strconv.Itoa(id), tokens[2], tokens[3], tokens[4], tokens[5]}) } else { fmt.Println("USAGE: lib add ") } } case "remove": if len(tokens) == 3 { lib.RemoveByName(tokens[2]) } else { fmt.Println("USAGE: lib remove ") } default: fmt.Println("Unrecognized lib command:", tokens[1]) }}func handlePlayCommand(tokens []string) { if len(tokens) != 2 { fmt.Println("USAGE: play ") return } e, _ := lib.Find(tokens[1]) if e == nil { fmt.Println("The music", tokens[1], "does not exist.") return } 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 -- Add a music to the music lib lib remove -- Remove the specified music from the lib play -- Play the specified music `) lib = NewMusicManager() r := bufio.NewReader(os.Stdin) for { fmt.Print("Enter command-> ") rawLine, _, _ := r.ReadLine() line := string(rawLine) if line == "quit" || line == "QUIT" { 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]) } }}

    来跑一跑感受一下吧!

    localhost:mediaplayer yekai$ go run *.go
    Enter following commands to control the player: lib list -- View the existing music lib lib add -- Add a music to the music lib lib remove -- Remove the specified music from the lib play -- Play the specified music Enter command-> lib listEnter command-> lib add 浮夸 陈奕迅 浮夸.mp3 MP3Enter command-> lib list1 : 浮夸 陈奕迅 浮夸.mp3 MP3Enter command-> play 浮夸Playing MP3 music 浮夸.mp3..........Finished playing 浮夸.mp3Enter command-> quitlocalhost:mediaplayer yekai$

    总结:这个案例核心逻辑在于接口的使用,从案例本身来说除了不能正常播放音乐外,也存在一些问题,比如当正在播放一个音乐的同时不能去做其他的事情,这就和Go语言的并发联系起来了,感兴趣的童鞋可以做一做,添加goroutinechannel的操作在里面!




    结尾福利:联系柏链助教周老师,可领取体验课视频一份,现在报名柏链VIP课程,可获千元优惠!



    新老学院月饼赠送活动:


    凡在815-915日入学柏链VIP课程的学员,都可联系柏链小道老师领取月饼礼盒一份,在此祝大家学习愉快!




    往期公开课回看方式:


主题帖 67 关注 0 粉丝 0
情感指数

链路大数据分析置信度 51.19 %

TA的主题帖
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表