学习设计模式——备忘录模式

概述

备忘录模式:(Memento Design Pattern)在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。

何时使用:

  • 状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。
  • 代码中包含大量与对象状态有关的条件语句。

什么是状态机?

  • 状态机有 3 个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也称为转移条件(Transition Condition)。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。

UML 类图:

image.png

角色组成:

  1. 上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。
  2. 抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
  3. 具体状态(Concrete State):实现抽象状态定义的接口。

通用代码

Context.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码public class Context {
private State state;

public Context() {
this.state = new ConcreteStateA(); // 设置初始状态
}

public State getState() {
return state;
}

public void setState(State state) {
this.state = state;
}

public void request() {
this.state.handle(this);
}

}

State.java

1
2
3
4
5
java复制代码public abstract class State {

public abstract void handle(Context context);

}

ConcreteStateA.java、ConcreteStateB.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
java复制代码public class ConcreteStateA extends State {

@Override
public void handle(Context context) {
System.out.println("ConcreteStateA 状态的 handle 方法");
context.setState(this);
}
}


public class ConcreteStateB extends State {

@Override
public void handle(Context context) {
System.out.println("ConcreteStateB 状态的 handle 方法");
context.setState(this);
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public class Test {
public static void main(String[] args) {

Context context = new Context();
context.request();
System.out.println("当前状态为:" + context.getState());

context.setState(new ConcreteStateB());
context.request();
System.out.println("当前状态为:" + context.getState());

}
}

结果:

ConcreteStateA 状态的 handle 方法

当前状态为:通用模板.ConcreteStateA@1b6d3586

ConcreteStateB 状态的 handle 方法

当前状态为:通用模板.ConcreteStateB@4554617c

具体实例

相信很多人都玩过王者荣耀这款手游,在对局中,王者荣耀里面的英雄遭受到不同的技能或者增益buff会有不同的状态,比如眩晕、加速、减速等等。如果不使用状态模式的话,我们的英雄类会非常的复杂和难以维护。此时使用状态模式才是更好的选择,下面用代码模拟实现一遍。

RunState.java

1
2
3
4
5
java复制代码public interface RunState {

void run(Hero hero);

}

CommonState.java、SpeedUpState.java、SpeedDownState.java、SwimState.java

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
java复制代码public class CommonState implements RunState {
@Override
public void run(Hero hero) {
// 正常跑动
}
}


public class SpeedUpState implements RunState {
@Override
public void run(Hero hero) {
System.out.println("--------------加速跑动---------------");
try {
Thread.sleep(4000);//假设加速持续4秒
} catch (InterruptedException e) {}
hero.setState(Hero.COMMON);
System.out.println("------加速状态结束,变为正常状态------");

}
}


public class SpeedDownState implements RunState{
@Override
public void run(Hero hero) {
System.out.println("--------------减速跑动---------------");
try {
Thread.sleep(4000);//假设减速持续4秒
} catch (InterruptedException e) {}
hero.setState(Hero.COMMON);
System.out.println("------减速状态结束,变为正常状态------");
}
}


public class SwimState implements RunState{
@Override
public void run(Hero hero) {
System.out.println("--------------不能跑动---------------");
try {
Thread.sleep(2000);//假设眩晕持续2秒
} catch (InterruptedException e) {}
hero.setState(Hero.COMMON);
System.out.println("------眩晕状态结束,变为正常状态------");
}
}

Hero.java

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
java复制代码public class Hero {

public static final RunState COMMON = new CommonState();//正常状态

public static final RunState SPEED_UP = new SpeedUpState();//加速状态

public static final RunState SPEED_DOWN = new SpeedDownState();//减速状态

public static final RunState SWIM = new SwimState();//眩晕状态

private RunState state = COMMON;//默认是正常状态

private Thread runThread;//跑动线程

//设置状态
public void setState(RunState state) {
this.state = state;
}

//停止跑动
public void stopRun() {
if (isRunning()) {
runThread.interrupt();
}
System.out.println("--------------停止跑动---------------");
}

//开始跑动
public void startRun() {
if (isRunning()) {
return;
}
final Hero hero = this;
runThread = new Thread(new Runnable() {
@Override
public void run() {
while (!runThread.isInterrupted()) {
state.run(hero);
}
}
});
System.out.println("--------------开始跑动---------------");
runThread.start();
}

private boolean isRunning() {
return runThread != null && !runThread.isInterrupted();
}

}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public class Test {
public static void main(String[] args) throws InterruptedException {
Hero hero = new Hero();
hero.startRun();
hero.setState(Hero.SPEED_UP);
Thread.sleep(5000);
hero.setState(Hero.SPEED_DOWN);
Thread.sleep(5000);
hero.setState(Hero.SWIM);
Thread.sleep(5000);
hero.stopRun();
}
}

结果:

————–开始跑动—————

————–加速跑动—————

——加速状态结束,变为正常状态——

————–减速跑动—————

——减速状态结束,变为正常状态——

————–不能跑动—————

——眩晕状态结束,变为正常状态——

————–停止跑动—————


总结

状态模式与策略模式区别
状态模式和策略模式的 UML 一样,但是解决的问题和侧重不一样。

  1. 状态模式重点在各状态之间的切换从而做不同的事情,而策略模式更侧重于根据具体情况选择策略,并不涉及切换。
  2. 状态模式不同状态下做的事情不同,而策略模式做的都是同一件事,例如聚合支付平台,有支付宝、微信支付、银联支付,虽然策略不同,但最终做的事情都是支付,也就是说他们之间是可替换的。反观状态模式,各个状态的同一方法做的是不同的事,不能互相替换。
  3. 状态模式封装了对象的状态,而策略模式封装算法或策略。因为状态是跟对象密切相关的,它不能被重用;而通过从Context中分离出策略或算法,我们可以重用它们。
  4. 在状态模式中,每个状态通过持有Context的引用,来实现状态转移;但是每个策略都不持有Context的引用,它们只是被Context使用。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%