状态模式的介绍:(重点就在这句)
状态模式中的行为是由状态来决定的,不同的状态下有不同的行为。
状态模式和策略模式的结构几乎是一样的,但是他们的目的,本质却完全不一样。
状态模式的行为是平行的,不可替换的
策略模式的行为是独立的,可以相互替换的
用一句话来表述,状态模式把对象的行为封装在不同的状态类中,每一个状态对象(状态类的实例)都有一个共同的抽象状态基类。
状态模式的意图是让一个对象在其内部状态改变时,其行为也随之改变
状态模式的定义
当一个对象的内部状态发生改变时允许其改变行为
状态模式使用场景:(下面两个场景很重要)
- 一个对象的行为取决于它的状态,并且它必须在运行时根据状态来改变其行为
- 代码中把包含大量与对象状态有关的条件语句,例如,一个操作中含有庞大的多分支语句 if else 或者 switch case ,且这些分支依赖于该对象的状态
状态模式是将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化,
这样通过多态来去除过多的,重复的if else 等分支语句
下面就以电视遥控器为例来演示一下状态模式的实现。我们首先将电视的状态简单分为开机状态和关机状态,在开机状态下,可以通过遥控器进行频道切换,调整音量等操作,但是,此时重复按开机键是无效的,而在关机状态下,频道切换,音量操作是无效的操作,只有按开机按钮时会生效,也就是说电视的内部状态决定了遥控器的行为。
结合上面的状态模式的定义:开机和关机就是所说的状态。频道切换和调整音量操作就是行为。状态能影响行为,和使用的场景(1)是可以对得上了。
状态改变,其行为也会改变,例如电视开机状态下,是可以把音量调大或者调小的,如果此时电视机状态变为关机了,音量就不可以调了。是不是状态改变了就可以影响到了行为了,这和上面的状态模式的介绍也可以对得上了。
是的,状态模式就是这样一个模式,行为受状态的影响。和策略模式有点像,但是要记得,是有本质区别的。
以上面的例子,看一下第一版的实现。
/**
*
* 电视遥控器,含有开机,关机,下一频道,上一频道,调高音量,调低音量
*/
public class TvController {
//开机状态
private final static int POWER_ON = 1;
//关机状态
private final static int POWER_OFF = 2;
private int mState = POWER_OFF;
//开机
public void powerOn(){
mState = POWER_ON;
if(mState == POWER_OFF){
System.out.println("开机啦");
}
}
//关机
public void powerOff(){
mState = POWER_OFF;
if(mState == POWER_ON){
System.out.println("关机啦");
}
}
//下一频道
public void nextChannel(){
if(mState == POWER_ON){
System.out.println("下一频道");
}else {
System.out.println("两个红灯提示没有开机");
}
}
//上一频道
public void preChannel(){
if(mState == POWER_ON){
System.out.println("上一频道");
}else {
System.out.println("两个红灯提示没有开机");
}
}
//调高音量
public void turnUp(){
if(mState == POWER_ON){
System.out.println("调高音量");
}else {
System.out.println("两个红灯提示没有开机");
}
}
//调低音量
public void turnDown(){
if(mState == POWER_ON){
System.out.println("调低音量");
}else {
System.out.println("两个红灯提示没有开机");
}
}
}
可以看到,在TvController类中,通过mState字段存储了电视的状态,并且在各个操作中根据状态来判断是否应该执行。这就导致了在每个功能中都需要使用if else,代码重复,相对较为混乱,这是在只有两个状态和几个简单的功能函数的情况下,如果状态变成5个,10个呢?每个函数都要有if else 判断,而这些代码都充斥在一个类中,这些重复的代码无法被提取出来,这使得这个类变得越来越难以维护。
状态模式就是为这类问题而出现的,我们将这些状态用状态对象来代替,将这些行为封装在状态类中,使得在不同的状态下有不同的实现,这样就将这些if else 从
TvController类中去掉,整个结构也简单变得清楚起来,我们看看实现的代码:
/**
* 电视状态接口,定义了电视操作的函数
*/
public interface TvState {
void nextChannel(); //下一个频道
void preChannel(); //上一个频道
void turnUp(); //调高音量
void turnDown(); //调低音量
}
开机状态
/**
* 开机状态
*/
public class PowerOnState implements TvState{
@Override
public void nextChannel() {
System.out.println("下一个频道");
}
@Override
public void preChannel() {
System.out.println("上一个频道");
}
@Override
public void turnUp() {
System.out.println("调高音量");
}
@Override
public void turnDown() {
System.out.println("调低音量");
}
}
关机状态
/**
* 关机状态
*/
public class PowerOffState implements TvState{
@Override
public void nextChannel() {
}
@Override
public void preChannel() {
}
@Override
public void turnUp() {
}
@Override
public void turnDown() {
}
}
电源操作接口
/**
* 电源操作接口
*/
public interface PowerControl {
void powerOn();
void powerOff();
}
电视遥控器
/**
* 遥控器
*/
public class TvControl implements PowerControl{
TvState mState;
public void setState(TvState state){
mState = state;
}
//改变为开机状态
@Override
public void powerOn() {
//改变为开机状态,把状态设置为开机状态
setState(new PowerOnState());
System.out.println("开机啦");
}
//改变为关机状态
@Override
public void powerOff() {
//改变为关机状态,把状态设置为开机状态
setState(new PowerOffState());
System.out.println("关机啦");
}
public void nextChannel(){
mState.nextChannel();
}
public void preChannel(){
mState.preChannel();
}
public void turnUp(){
mState.turnUp();
}
public void turnDown(){
mState.turnDown();
}
}
下面是客户端测试类
/**
* 客户端测试类
*/
public class StateTest {
public static void main(String[] args){
test();
}
public static void test(){
//电视
TvControl tvControl = new TvControl();
//改变状态,看看是否影响行为
tvControl.powerOn();
//下一个频道
tvControl.nextChannel();
//调大音量
tvControl.turnUp();
//设置关机状态
tvControl.powerOff();
//调高音量,此时不会生效
tvControl.turnUp();
}
}
运行结果如下:
开机啦
下一个频道
调高音量
关机啦
果然,状态改变时,行为也跟着改变了。