“
尺有所短,寸有所长;不忘初心,方得始终。
一、状态模式是什么
在软件开发过程中,应用程序中的某些对象会根据不同的情况做出不同的行为,这种对象称为有状态的对象,而把影响对象行为的不同的情况称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而影响其行为。
-
【定义】
对有状态的对象,把复杂的判断逻辑提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。属于行为型模式。
“
不同的类表示不同情况的状态,以此消除 if-else、switch-case 等冗余语句,代码更有层次性,并且具备良好的扩展性。
-
【主要作用】
可以根据对象所依赖状态的改变而改变它的相关行为。
-
【核心思想】
程序在任意时刻仅可处于几种有限的状态中。 在任何一个特定状态中, 程序的行为都不相同, 且可瞬间从一个状态切换到另一个状态。
二、状态模式的适用场景
-
【适用场景】
-
一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
-
一个操作中含有很多的多分支结构,并且这些分支取决于对象的状态。
-
【生活实例】
-
人的高兴、伤心的不同情绪有不同的行为。 -
线程有新建、运行、阻塞,等待状态,超时等待状态和死亡 6 种状态。 -
播放器的暂停、播放。 -
手机的解锁,锁定状态。
三、状态模式结构
-
上下文(Context)角色:定义了客户端需要的接口,内部持有一个具体状态对象的引用,并负责具体状态的切换。
-
抽象状态(State)角色:声明特定于状态方法的接口,用以封装上下文角色中的特定状态所对应的行为,可以有一个或多个行为。
-
具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
“
当多个状态有部分相似行为方法时, 可以提供一个封装有部分通用行为的中间抽象类。
-
四、状态模式实现方式
-
确定上下文类,定义状态转变的方法,并定义对抽象状态的引用。
-
定义抽象状态角色,声明状态变化之后的行为方法。
-
为每个实际状态创建一个继承于状态接口的类。在每个具体状态类中实现该状态对应的。
当多个状态有部分相似行为方法时, 可以提供一个封装有部分通用行为的中间抽象类。
-
将通用成员变量或方法设为公有。 -
抽取的上下文行为更改为上下文中的公有方法, 然后在状态类中调用。 -
定义上下文角色,初始化状态,行为执行。
五、状态模式的实现
【案例】:线程的状态切换。

【案例说明】:在我们日常开发工作中,线程是经常会是用的一种技术手段,线程有新建、运行、阻塞,等待状态,超时等待状态和死亡 6 种状态,在不同的状态下有不同的行为。在此案例中:
-
上下文(Context)角色持有一个线程的当前状态,并为其初始化状态,提供转换到其他状态的方法。 -
抽象状态(State)角色只作为一个基类,指定状态名字,具体实现由具体的状态类实现。 -
具体状态(Concrete State)角色实现每个状态的具体行为
-
上下文(Context)角色
import lombok.Data;
/**
* 上下文(Context)角色 提供转换到其他状态的方法
* @author Edwin
* @date 2021/11/21 15:55
*/
@Data
public class Context {
/**
* 持有一个线程的当前状态
* @author Edwin
* @date 2021/11/21 17:08
*/
private TheadState state;
/**
* 初始化线程的当前状态
* @author Edwin
* @date 2021/11/21 17:09
*/
Context() {
state = new TheadNew();
}
/**
* 开启一个新的线程
* @author Edwin
* @date 2021/11/21 17:11
*/
public void start() {
((TheadNew) state).start(this);
}
/**
* 阻塞状态线程恢复到运行状态
* @author Edwin
* @date 2021/11/21 17:03
*/
public void resume() {
((TheadBlocked) state).resume(this);
}
/**
* 阻塞当前线程
* @author Edwin
* @date 2021/11/21 16:57
*/
public void sync() {
((TheadRunnable) state).sync(this);
}
/**
* 当前线程进入等待状态
* @author Edwin
* @date 2021/11/21 17:00
*/
public void await() {
((TheadRunnable) state).wait(this);
}
/**
* 当前线程进入超时等待状态状态
* @author Edwin
* @date 2021/11/21 17:00
*/
public void sleep() {
((TheadRunnable) state).sleep(this);
}
/**
* 停止线程
* @author Edwin
* @date 2021/11/21 16:54
*/
public void stop() {
((TheadRunnable) state).stop(this);
}
/**
* 线程在睡眠指定时间后自动唤醒恢复到运行状态
* @author Edwin
* @date 2021/11/21 17:00
* @return null
*/
public void wake() {
((TheadTimedwaiting) state).wake(this);
}
/**
* 线程恢复到运行状态
* @author Edwin
* @date 2021/11/21 17:00
*/
public void notice() {
((TheadWaiting) state).notify(this);
}
} -
抽象状态(State)角色
/**
* 抽象状态(State)角色:线程状态
* @author Edwin
* @date 2021/11/21 15:56
*/
public abstract class ThreadState {
/**
* 线程的当前状态名字,没有什么具体实际意义
* @author Edwin
* @date 2021/11/21 16:02
*/
public String threadName;
} -
线程状态枚举
Java中Thread类中State枚举类定义与说明
public enum State {
/**
* 新建状态
*/
NEW,
/**
* 运行状态
* java线程中将操作系统中的就绪状态(ready)和运行中状态(running)统一归类为可运行状态(RUNNABLE)。
*/
RUNNABLE,
/**
* 阻塞状态
*/
BLOCKED,
/**
* 等待状态
* 进入该状态的线程需要等待其它线程进行一些特殊操作,如notify、notifyAll、unpark
*
* 触发条件:调用下列任何一个方法
* Object#wait()
* join()
* LockSupport#park()
*/
WAITING,
/**
* 超时等待状态
* 该状态与WAITING不同之处是在指定时间后可以自动唤醒到就绪状态;
*
* 触发条件:调用下列任何一个方法
* sleep Thread.sleep
* Object#wait(long)
* join(long)
* LockSupport#parkNanos
* LockSupport#parkUntil
*/
TIMED_WAITING,
/**
* 终止状态
*/
TERMINATED;
} -
具体状态(Concrete State)角色
/**
* 具体状态(Concrete State)角色 : 新建状态
* @author Edwin
* @date 2021/11/21 16:04
*/
public class ThreadNew extends ThreadState {
/**
* 初始化新建状态
* @author Edwin
* @date 2021/11/21 16:05
*/
public ThreadNew() {
threadName = Thread.State.NEW.name();
System.out.println("当前线程处于:新建状态.");
}
/**
* 调用线程的start方法 开启一个新的线程
* @author Edwin
* @date 2021/11/21 16:44
*/
public void start(Context context) {
System.out.print("调用start()方法,获取CPU的执行时间-->");
if (Thread.State.NEW.name().equals(threadName)) {
context.setState(new ThreadRunnable());
} else {
System.out.println("当前线程不是新建状态,不能调用start()方法.");
}
}
}/**
* 具体状态(Concrete State)角色 : 运行状态
* @author Edwin
* @date 2021/11/21 16:04
*/
public class ThreadRunnable extends ThreadState {
/**
* 运行状态
* @author Edwin
* @date 2021/11/21 16:05
*/
public ThreadRunnable() {
threadName = Thread.State.RUNNABLE.name();
System.out.println("当前线程处于:运行状态.");
}
/**
* 模拟 synchronized 进入阻塞状态
* @author Edwin
* @date 2021/11/21 16:57
*/
public void sync(Context context) {
if (ThreadStateEnum.RUNNABLE.name().equals(threadName)) {
System.out.print("调用sync()方法,线程进入阻塞状态-->");
context.setState(new ThreadBlocked());
} else {
System.out.println("当前线程不是运行状态,不能调用sync()方法.");
}
}
/**
* 模拟当前线程对象调用wait()方法。进入等待状态
* @author Edwin
* @date 2021/11/21 17:00
*/
public void wait(Context context) {
if (Thread.State.RUNNABLE.name().equals(threadName)) {
System.out.print("调用wait()方法,线程进入等待状态-->");
context.setState(new ThreadWaiting());
} else {
System.out.println("当前线程不是运行状态,不能调用wait()方法.");
}
}
/**
* 模拟当前线程对象调用Thread.sleep(long)方法。进入超时等待状态状态
* @author Edwin
* @date 2021/11/21 17:00
*/
public void sleep(Context context) {
System.out.print("调用sleep()方法,线程进入超时等待状态-->");
if (Thread.State.RUNNABLE.name().equals(threadName)) {
context.setState(new ThreadTimedwaiting());
} else {
System.out.println("当前线程不是运行状态,不能调用sleep()方法.");
}
}
/**
* 调用stop() 停止线程
* @author Edwin
* @date 2021/11/21 16:54
*/
public void stop(Context context) {
if (Thread.State.RUNNABLE.name().equals(threadName)) {
System.out.print("调用stop()方法,停止当前线程-->");
context.setState(new ThreadTerminated());
} else {
System.out.println("当前线程不是运行状态,不能调用stop()方法.");
}
}
}/**
* 具体状态(Concrete State)角色 : 阻塞状态
* @author Edwin
* @date 2021/11/21 16:04
*/
public class ThreadBlocked extends ThreadState {
/**
* 阻塞状态
* @author Edwin
* @date 2021/11/21 16:05
* @return null
*/
public ThreadBlocked() {
threadName = Thread.State.BLOCKED.name();
System.out.println("当前线程处于:阻塞状态.");
}
/**
* 阻塞状态线程取得锁 恢复到运行状态
* @author Edwin
* @date 2021/11/21 17:03
*/
public void resume(Context context) {
if (Thread.State.BLOCKED.name().equals(threadName)) {
System.out.print("调用resume()方法,线程恢复到运行状态-->");
context.setState(new ThreadRunnable());
} else {
System.out.println("当前线程不是阻塞状态,不能调用resume()方法.");
}
}
}/**
* 具体状态(Concrete State)角色 : 等待状态
* @author Edwin
* @date 2021/11/21 16:04
*/
public class ThreadWaiting extends ThreadState {
/**
* 等待状态
* @author Edwin
* @date 2021/11/21 16:05
*/
public ThreadWaiting() {
threadName = Thread.State.WAITING.name();
System.out.println("当前线程处于:等待状态.");
}
/**
* 模拟其它线程调用notify、notifyAll、unpark 线程恢复到运行状态
* @author Edwin
* @date 2021/11/21 17:00
*/
public void notify(Context context) {
if (Thread.State.WAITING.name().equals(threadName)) {
System.out.print("调用notify()方法,线程恢复到运行状态-->");
context.setState(new ThreadRunnable());
} else {
System.out.println("当前线程不是运行状态,不能调用notify()方法.");
}
}
}/**
* 具体状态(Concrete State)角色 : 超时等待状态
* @author Edwin
* @date 2021/11/21 16:04
*/
public class ThreadTimedwaiting extends ThreadState {
/**
* 超时等待状态
* @author Edwin
* @date 2021/11/21 16:05
* @return null
*/
public ThreadTimedwaiting() {
threadName = Thread.State.TIMED_WAITING.name();
System.out.println("当前线程处于:超时等待状态.");
}
/**
* 模拟线程在睡眠指定时间后自动唤醒恢复到运行状态
* @author Edwin
* @date 2021/11/21 17:00
*/
public void wake(Context context) {
if (ThreadStateEnum.TIMED_WAITING.name().equals(threadName)) {
System.out.print("调用wake()方法,线程自动唤醒恢复到运行状态-->");
context.setState(new ThreadRunnable());
} else {
System.out.println("当前线程不是运行状态,不能调用wake()方法.");
}
}
}/**
* 具体状态(Concrete State)角色 : 终止状态
* @author Edwin
* @date 2021/11/21 16:04
*/
public class ThreadTerminated extends ThreadState {
/**
* 终止状态
* @author Edwin
* @date 2021/11/21 16:05
*/
public ThreadTerminated() {
threadName = Thread.State.TERMINATED.name();
System.out.println("当前线程处于:终止状态.");
}
} -
运行状态 -
终止状态 -
超时等待状态 -
等待状态 -
阻塞状态 -
运行状态 -
新建状态 -
客户端代码实现
public static void main(String[] args) {
// 定义上下文角色
System.out.print("定义上下文角色=========>>>>>>>>>");
Context context = new Context();
System.out.print("执行当前线程===========>>>>>>>>>");
context.start();
System.out.print("当前线程进入等待状态=======>>>>>>");
context.await();
System.out.print("唤醒当前线程进入运行状态====>>>>>>");
context.notice();
System.out.print("当前线程进入阻塞状态======>>>>>>>");
context.sync();
System.out.print("阻塞线程获得锁进入运行状态====>>>>");
context.resume();
System.out.print("当前线程进入等待状态状态=====>>>>>");
context.sleep();
System.out.print("当前睡眠线程进入运行状态=========>>>>>>>>>");
context.wake();
System.out.print("停止线程当前线程=========>>>>>>>>>");
context.stop();
} -
六、状态模式的优缺点
-
优点
-
满足单一职责原则,特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,结构清晰。
-
状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。 -
状态转换显示化,减少对象间的相互依赖。通过消除臃肿的状态机条件语句简化上下文代码。 -
缺点
-
状态模式的使用必然会增加系统的类与对象的个数。 -
状态模式的结构与实现都较为复杂,增加系统的复杂度。 -
状态模式对开闭原则的支持并不太好,增加新的状态类和行为都需要修改源码。
七、状态模式和其他模式的区别
-
【状态模式】和【责任链模式】都能消除 if-else 分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用。
-
【状态模式】强调的是一个对象内在状态的改变,各个状态对象知道自己要进入的下一个状态对象。 -
【责任链模式】强调的是外部节点对象间的改变,并不清楚其下一个节点处理对象,链式组装由客户端负责。 -
【状态模式】和【策略模式】的 UML 类图架构几乎一样,但应用场景不一样
-
【策略模式】多种算法行为是独立的,任何一种都能满足业务,客户端可自行更换策略算法。 -
【状态模式】各个状态在一定条件下可以自动切换到其他状态,客户端只能设置初始状态,无法指定状态。
八、总结
状态模式的使用免去了过多的if–else判断,但是会造成更多的接口和类,所以对于非常简单的状态判断,不建议使用。当状态的个数有限并且相互独立的时候可以考虑使用状态模式。
原文始发于微信公众号(星河之码):设计模式(21):状态模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/27085.html