一、简介
状态模式(State Pattern) 顾名思义,物体会根据自身某个属性的状态的改变,来引发相同的动作导致不同的结果。
定义:类的行为会根据不同的状态发生改变。
使用场景:音乐播放器的状态,有正在播放、上一首、下一首、暂停、停止,我们可以控制播放器的状态,从而控制音乐是否播放。
实现要素:
- 将 状态 抽成一个类,并且将具体的行为也抽取到其中。
- 状态之间可以相互切换。例如:点击播放器的 播放,播放器的状态由 暂停 变成 播放。
- 一定要区分 状态模式 和 策略模式 !!!
如果某个类需要根据成员变量的当前值改变自身行为, 从而需要使用大量的条件语句时, 可使用该模式,状态模式 会将这些条件语句的分支抽取到相应状态类的方法中。 同时, 你还可以清除主要类中与特定状态相关的临时成员变量和帮手方法代码。
二、实现
我们就以音乐播放器为例子,来看看其实现的过程。
1、普通实现
class Mp3Player {
//播放器状态
private lateinit var state:String
//歌曲列表
private val songList = mutableListOf<String>()
//当前播放的位置
private var currentSongPosition: Int? = null
//初始化
init {
state = "停止"
songList.addAll(listOf("《哥练的胸肌》","《说好不哭》","《mojito》"))
}
/**
* 播放/暂停按钮点击事件
*/
fun playOrPause(){
when(state){
"停止"->{
currentSongPosition = 0
println("播放=>${songList[currentSongPosition!!%songList.size]}")
state = "播放中"
}
"播放中"->{
//变成暂停
state = "暂停"
println("暂停播放=>${songList[currentSongPosition!!%songList.size]}")
}
"暂停"->{
state = "播放中"
println("继续播放=>${songList[currentSongPosition!!%songList.size]}")
}
}
}
/**
* 停止按钮点击使劲按
*/
fun stop(){
when(state){
"停止","播放中","暂停"->{
state = "停止"
currentSongPosition = 0
println("停止播放")
}
}
}
/**
* 下一首
*/
fun nextSong(){
when(state){
"停止"->{
//啥也不做
}
"播放中","暂停"->{
//变成暂停
currentSongPosition = currentSongPosition?.plus(1)
state = "播放中"
println("播放下一首=>${songList[currentSongPosition!!%songList.size]}")
}
}
}
/**
* 上一首
*/
fun preSong(){
when(state){
"停止"->{
//啥也不做
}
"播放中","暂停"->{
//变成暂停
currentSongPosition = currentSongPosition?.minus(1)
state = "播放中"
println("播放上一首=>${songList[currentSongPosition!!%songList.size]}")
}
}
}
}
我们在 main 函数中模拟一下:
fun main() {
val mp3Player = Mp3Player()
mp3Player.playOrPause()
mp3Player.playOrPause()
mp3Player.playOrPause()
mp3Player.nextSong()
mp3Player.stop()
mp3Player.nextSong() //注意控制台输出,没有任何效果
mp3Player.preSong() //注意控制台输出,没有任何效果
mp3Player.playOrPause()
mp3Player.nextSong()
mp3Player.nextSong()
}
控制台输出:
播放=>《哥练的胸肌》
暂停播放=>《哥练的胸肌》
继续播放=>《哥练的胸肌》
播放下一首=>《说好不哭》
停止播放
播放=>《哥练的胸肌》
播放下一首=>《说好不哭》
播放下一首=>《mojito》
2、使用状态模式
1、我们将状态抽出来
所有的播放器行为抽成一个接口。
interface Mp3State{
fun playOrPause()
fun stop()
fun nextSong()
fun preSong()
}
2、使播放器继承该接口,并且定义一个状态属性
并且在各个需要实现的方法中,直接使状态属性调用相应的方法。 这是因为 状态模式 的不同状态行为的具体实现是在每个不同的 状态 中。
class Mp3Player :Mp3State {
//播放器状态
private lateinit var state: Mp3State
//歌曲列表
private val songList = mutableListOf<String>()
//当前播放的位置
private var currentSongPosition: Int? = null
init {
//当你把各个状态定义好了之后,就可以初始化 State
state = StopState(this)
songList.addAll(listOf("《哥练的胸肌》","《说好不哭》","《mojito》"))
}
/**
* 改变状态
*/
fun changeState(state: Mp3State){
this.state = state
}
override fun playOrPause() {
state?.playOrPause()
}
override fun stop() {
state?.stop()
}
override fun nextSong() {
state?.nextSong()
}
override fun preSong() {
state?.preSong()
}
}
3、实现不同的状态
// 停止状态
class StopState(val player: Mp3Player) :Mp3State{
override fun playOrPause() {
//改变成播放状态
player.changeState(PlayState(player))
player.currentSongPosition = 0
println("播放=>${player.songList[player.currentSongPosition!!%player.songList.size]}")
}
override fun stop() {
//停止状态下,不做任何事情。
}
override fun nextSong() {}
override fun preSong() {}
}
// 播放中状态
class PlayState(val player: Mp3Player) :Mp3State{
override fun playOrPause() {
//改变成暂停状态
player.changeState(PauseState(player))
println("暂停播放=>${player.songList[player.currentSongPosition!!%player.songList.size]}")
}
override fun stop() {
player.changeState(StopState(player))
player.currentSongPosition = null
println("停止播放")
}
override fun nextSong() {
player.currentSongPosition = player.currentSongPosition?.plus(1)
println("播放下一首=>${player.songList[player.currentSongPosition!!%player.songList.size]}")
}
override fun preSong() {
player.currentSongPosition = player.currentSongPosition?.minus(1)
println("播放上一首=>${player.songList[player.currentSongPosition!!%player.songList.size]}")
}
}
// 暂停状态
class PauseState(val player: Mp3Player) :Mp3State{
override fun playOrPause() {
//改变成播放状态
player.changeState(PlayState(player))
println("继续播放=>${player.songList[player.currentSongPosition!!%player.songList.size]}")
}
override fun stop() {
player.changeState(StopState(player))
player.currentSongPosition = null
println("停止播放")
}
override fun nextSong() {
player.changeState(PlayState(player))
player.currentSongPosition = player.currentSongPosition?.plus(1)
println("播放下一首=>${player.songList[player.currentSongPosition!!%player.songList.size]}")
}
override fun preSong() {
player.changeState(PlayState(player))
player.currentSongPosition = player.currentSongPosition?.minus(1)
println("播放上一首=>${player.songList[player.currentSongPosition!!%player.songList.size]}")
}
}
在 main 函数中:
fun main() {
val mp3Player = Mp3Player()
mp3Player.playOrPause()
mp3Player.playOrPause()
mp3Player.stop()
mp3Player.nextSong()
mp3Player.playOrPause()
mp3Player.nextSong()
mp3Player.nextSong()
mp3Player.playOrPause()
mp3Player.playOrPause()
}
控制台输出:
播放=>《哥练的胸肌》
暂停播放=>《哥练的胸肌》
停止播放
播放=>《哥练的胸肌》
播放下一首=>《说好不哭》
播放下一首=>《mojito》
暂停播放=>《mojito》
继续播放=>《mojito》
三、相关源码
太难找了……有知道的小伙伴欢迎在留言区提供线索。
四、小结
状态模式 的精髓就是:状态之间是可以相互切换的,这也是 状态模式 和 策略模式 之间最大的区别!这两种模式的 UML 图几乎一模一样,如果对两种模式不是很熟悉的话,会很容易弄混淆。
状态模式 看起来似乎会经常用到,用来代替 if else 和 switch 语句,实际上真正能有适合的场景比较少,不是你看到 if else 或者 switch 语句就能够使用 状态模式,在这基础上,你还要根据类的具体业务逻辑来判断,是否可以使用该模式。