对状态模式的理解
- 一、场景
- 二、不采用状态模式
- 1、代码
- 2、缺点
- 三、采用状态模式
- 1、代码
- 1.1 状态类
- 1.2 上下文(这里指:媒体播放器)
- 1.3 客户端
- 2、优点
一、场景
-
同一个东西(例如:媒体播放器),有一些操作:暂停、播放、停止。很显然,每执行一种操作,媒体播放器的状态便改变了。
-
播放 -> 暂停:
- Media Player is now paused.
当前是播放状态,点击暂停操作,输出:Media Player is not playing.
-
暂停 -> 暂停
- Media Player is not playing.
当前是暂停状态,点击暂停操作,输出:Media Player is not playing.
-
虽然是同一种操作,但是,因为媒体播放器处在不同的状态,所以表现不同。
-
-
面对这种场景,我们怎么编码呢?
-
简单粗暴的方式:写if-else或者switch。(详见:二、不采用状态模式)
-
更好的方式:采用状态模式。(详见:三、采用状态模式)
- 状态模式是一种行为设计模式, 让我们能在一个对象的内部状态变化时改变其行为。
-
二、不采用状态模式
1、代码
// 媒体播放器
public class MediaPlayer {private String currentState;public MediaPlayer() {this.currentState = "STOPPED"; // 初始状态为停止}public void play() {if (currentState.equals("STOPPED")) {System.out.println("Media Player is now playing.");currentState = "PLAYING";} else if (currentState.equals("PAUSED")) {System.out.println("Media Player resumed playing.");currentState = "PLAYING";} else {System.out.println("Media Player is already playing.");}}public void pause() {if (currentState.equals("PLAYING")) {System.out.println("Media Player is now paused.");currentState = "PAUSED";} else {System.out.println("Media Player is not playing.");}}public void stop() {System.out.println("Media Player is now stopped.");currentState = "STOPPED";}
}// 客户端
public class Main {public static void main(String[] args) {MediaPlayer mediaPlayer = new MediaPlayer();System.out.println("Initial state: Stopped");mediaPlayer.play(); // Stopped -> PlayingmediaPlayer.pause(); // Playing -> PausedmediaPlayer.play(); // Paused -> PlayingmediaPlayer.stop(); // Playing -> Stopped}
}/*
Initial state: Stopped
Media Player is now playing.
Media Player is now paused.
Media Player resumed playing.
Media Player is now stopped.
*/
2、缺点
- 由于在设计之初,不一定能想到所有状态,假设一开始只想到了:play、pause、stop。辛辛苦苦写好了上面的代码。这时候,产品经理又提了新需求了,发现又要补2个状态。没办法,上面的play、pause、stop三个方法都要进行修改。改着改着就成屎山了。
三、采用状态模式
1、代码
1.1 状态类
- 状态模式建议为对象的所有可能状态新建一个类, 每个状态独自实现自身状态下的各个行为。
// 抽象父类
public abstract class MediaPlayerState {protected MediaPlayer mediaPlayer;public MediaPlayerState(MediaPlayer mediaPlayer) {this.mediaPlayer = mediaPlayer;}protected abstract void play();protected abstract void pause();protected abstract void stop();
}// 播放状态
public class MediaPlayerPlayState extends MediaPlayerState {public MediaPlayerPlayState(MediaPlayer mediaPlayer) {super(mediaPlayer);}@Overridepublic void play() {System.out.println("Media Player is already playing.");mediaPlayer.setState(this);}@Overridepublic void pause() {System.out.println("Media Player is now paused.");mediaPlayer.setState(new MediaPlayerPauseState(mediaPlayer));}@Overridepublic void stop() {System.out.println("Media Player is now stopped.");mediaPlayer.setState(new MediaPlayerStopState(mediaPlayer));}
}// 暂停状态
public class MediaPlayerPauseState extends MediaPlayerState {public MediaPlayerPauseState(MediaPlayer mediaPlayer) {super(mediaPlayer);}@Overridepublic void play() {System.out.println("Media Player resumed playing.");mediaPlayer.setState(new MediaPlayerPlayState(mediaPlayer));}@Overridepublic void pause() {System.out.println("Media Player is not playing.");mediaPlayer.setState(this);}@Overridepublic void stop() {System.out.println("Media Player is now stopped.");mediaPlayer.setState(new MediaPlayerStopState(mediaPlayer));}
}// 停止状态
public class MediaPlayerStopState extends MediaPlayerState {public MediaPlayerStopState(MediaPlayer mediaPlayer) {super(mediaPlayer);}@Overridepublic void play() {System.out.println("Media Player is now playing.");mediaPlayer.setState(new MediaPlayerPlayState(mediaPlayer));}@Overridepublic void pause() {System.out.println("Media Player is not playing.");mediaPlayer.setState(new MediaPlayerPauseState(mediaPlayer));}@Overridepublic void stop() {System.out.println("Media Player is now stopped.");mediaPlayer.setState(this);}
}
1.2 上下文(这里指:媒体播放器)
public class MediaPlayer {private MediaPlayerState state;public void setState(MediaPlayerState state) {this.state = state;}public MediaPlayer() {this.state = new MediaPlayerStopState(this);}public void play() {state.play();}public void pause() {state.pause();}public void stop() {state.stop();}
}
1.3 客户端
public class Main {public static void main(String[] args) {MediaPlayer mediaPlayer = new MediaPlayer();System.out.println("Initial state: Stopped");mediaPlayer.play(); // Stopped -> PlayingmediaPlayer.pause(); // Playing -> PausedmediaPlayer.play(); // Paused -> PlayingmediaPlayer.stop(); // Playing -> Stopped}
}/*
Initial state: Stopped
Media Player is now playing.
Media Player is now paused.
Media Player resumed playing.
Media Player is now stopped.
*/
2、优点
- 每个状态类,只需要关心自己状态下,每种操作应该执行哪些逻辑就好了。
- 如果新增了状态,那无非就是新增一个类,其他已有的状态类,无非就是新写一个方法。这并不会去修改已有的代码,符合开闭原则。
上面的写法有一个小瑕疵,每个状态类实际上应该是单例模式(详见:对单例模式的饿汉式、懒汉式的思考),而不是每次切换状态的时候新建一个对象。
-
不同状态下,有些操作是一样的,例如:停止操作。这时候可以采用组合的方式进行复用。将停止操作放到一个类中,例如:MediaPlayerStopHandler。每个方法调用MediaPlayerStopHandler的stop方法()实现停止。
-
仔细看下状态模式的结构:
-
会发现和策略模式的结构很像。但二者有很多不同:
-
(1)策略模式:
- 1)根据客户端的输入,匹配一种策略。每个策略完全独立,策略A感知不到策略B。
- 2)策略A和策略B是做同一件事情,仅仅是做法不同。
-
(2)状态模式:
- 1)每种状态下,会有多种行为。(如果只有一种行为,也不存在状态转换了)
- 2)状态A和状态B不是完全独立的,状态A执行某个行为后,会从状态A转移到状态B。
-
-