状态模式

定义

允许一个对象在其内部状态改变时,改变它的行为;
行为型

适用场景

  • 一个对象存在多个状态(不同状态下行为不同),且状态可相互转换

优点和缺点

优点

  • 将不同的状态隔离
  • 把各种状态的转换逻辑,分布到State子类中,减少相互间依赖
  • 增加新的状态非常简单

缺点

  • 状态对的业务场景等导致类数据增加,系统变复杂

结构

  • 环境类角色:也称为上下文,它定义了客户端需要大的接口,内部维护一个当前状态,并负责具体的状态切换;
  • 抽象状态角色:定义一个接口,用以封装环境对象中大的特定状态所对应的行为,可以有一个或多个行为。
  • 具体状态角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

代码实现

假设我们播放一个视频,一共有四种状态,播放、快进、暂停、停止,这四种状态,有的可以相互切换,有的不行
环境类角色,上下文,缓存一个当前状态,并负责切换状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class VideoContext {

private VideoState videoState;

public VideoState getVideoState() {
return videoState;
}

public void setVideoState(VideoState videoState) {
this.videoState = videoState;
this.videoState.setVideoContext(this);
}

public void play() {
this.videoState.play();
}

public void speed() {
this.videoState.speed();
}

public void pause() {
this.videoState.pause();
}

public void stop() {
this.videoState.stop();
}
}

抽象状态角色,组合有上下文类,方便子类使用

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class VideoState {
protected VideoContext videoContext;

public void setVideoContext(VideoContext videoContext) {
this.videoContext = videoContext;
}

public abstract void play();//播放
public abstract void speed();//快进
public abstract void pause();//暂停
public abstract void stop();//停止
}

具体状态角色
播放状态,可以和另外三种状态进行切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PlayState extends VideoState {

@Override
public void play() {
System.out.println("正常播放视频状态");
}

@Override
public void speed() {
super.videoContext.setVideoState(new SpeedState());
}

@Override
public void pause() {
super.videoContext.setVideoState(new PauseState());
}

@Override
public void stop() {
super.videoContext.setVideoState(new StopState());
}
}

快进状态,可以和另外三种状态切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SpeedState extends VideoState{

@Override
public void play() {
super.videoContext.setVideoState(new PlayState());
}

@Override
public void speed() {
System.out.println("快进播放视频状态");
}

@Override
public void pause() {
super.videoContext.setVideoState(new PauseState());
}

@Override
public void stop() {
super.videoContext.setVideoState(new StopState());
}

}

暂停状态,可以和另外三种状态切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class PauseState extends VideoState{

@Override
public void play() {
super.videoContext.setVideoState(new PlayState());
}

@Override
public void speed() {
super.videoContext.setVideoState(new SpeedState());
}

@Override
public void pause() {
System.out.println("暂停播放视频状态");
}

@Override
public void stop() {
super.videoContext.setVideoState(new StopState());
}

}

停止状态,只能和播放状态切换,停止的情况下不能快进和暂停

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StopState extends VideoState{

@Override
public void play() {
super.videoContext.setVideoState(new PlayState());
}

@Override
public void speed() {
System.out.println("ERROR, 停止状态不能快进");
}

@Override
public void pause() {
System.out.println("ERROR,停止状态不能暂停");
}

@Override
public void stop() {
System.out.println("停止播放视频状态");
}

}

测试,视频开始是播放状态,我们有事需要离开一会,可以切换成暂停状态,回来想要快进看,由暂停直接切换到快进状态,看完之后切换到停止状态,从停止状态再切换到快进状态是不允许的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {

public static void main(String[] args) {
VideoContext videoContext = new VideoContext();
videoContext.setVideoState(new PlayState());

System.out.println("当前状态:"+videoContext.getVideoState().getClass().getSimpleName());

videoContext.pause();
System.out.println("当前状态:"+videoContext.getVideoState().getClass().getSimpleName());

videoContext.speed();
System.out.println("当前状态:"+videoContext.getVideoState().getClass().getSimpleName());

videoContext.stop();
System.out.println("当前状态:"+videoContext.getVideoState().getClass().getSimpleName());

videoContext.speed();
}

}

结果:

1
2
3
4
5
当前状态:PlayState
当前状态:PauseState
当前状态:SpeedState
当前状态:StopState
ERROR, 停止状态不能快进

UML类图