16. 状态模式
约 2004 字大约 7 分钟
2026-03-11
定义
状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
人话:状态模式就像一个人上班一天的状态变化。早上刚到公司时精神很好,写代码效率很高;到了中午开始犯困,只想着吃饭和午休;下午状态还不错,可以继续工作;到了晚上如果任务没完成就只能加班,太晚了就直接困得不行想睡觉。
如果把这些情况都写在一个类里,通常就会出现很多 if-else 判断,比如判断当前时间是不是上午、是不是下午、任务有没有完成等等。随着状态越来越多,这些判断就会变得非常复杂,代码也越来越难维护,这在软件中就属于逻辑混乱、条件分支过多。
而使用状态模式之后,我们会把 “上午状态”“中午状态”“下午状态”“加班状态”“睡眠状态” 等分别封装成不同的状态类。对象只需要记录当前处于哪一种状态,当时间变化或任务完成时,就切换到下一个状态。这样对象在不同状态下就会表现出不同的行为,而不是依靠一大堆 if-else 判断来控制逻辑。
状态模式(State)结构图
State类——抽象状态类:
abstract class State
{
public abstract void Handle(Context context);
}ConcreteStateA类——具体状态类:
class ConcreteStateA : State
{
public override void Handle(Context context)
{
// 设置 ConcreteStateA 的下一状态为 ConcreteStateB
context.State = new ConcreteStateB();
}
}ConcreteStateB类——具体状态类:
class ConcreteStateB : State
{
public override void Handle(Context context)
{
// 设置 ConcreteStateB 的下一状态为 ConcreteStateA
context.State = new ConcreteStateA();
}
}Context类——维护当前状态:
class Context
{
private State state;
// 定义 Context 的初始状态
public Context(State state)
{
this.state = state;
}
// 可读写的状态属性
// 用于读取当前状态和设置新状态
public State State
{
get { return state; }
set
{
state = value;
Console.WriteLine("当前状态:" + state.GetType().Name);
}
}
// 对请求做处理,并设置下一状态
public void Request()
{
state.Handle(this);
}
}客户端代码:
static void Main(string[] args)
{
// 设置 Context 的初始状态为 ConcreteStateA
Context c = new Context(new ConcreteStateA());
// 不断请求,同时不断改变状态
c.Request();
c.Request();
c.Request();
c.Request();
Console.Read();
}状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用‘状态模式’了。
状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。
这样做的目的就是为了消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,就像我们最早说的刻版印刷一样,任何改动和变化都是致命的。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖,好比把整个版面改成了一个又一个的活字,此时就容易维护和扩展了。
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。另外如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是依靠大量的多分支判断语句来实现,此时应该考虑将每一种业务状态定义为一个State的子类。这样这些对象就可以不依赖于其他对象而独立变化了,某一天客户需要更改需求,增加或减少业务状态或改变状态流程,对你来说都是不困难的事。
举例
状态模式
State类——抽象状态类:
// 抽象状态
public abstract class State
{
public abstract void WriteProgram(Work w);
}上午工作状态:
// 上午工作状态
public class ForenoonState : State
{
public override void WriteProgram(Work w)
{
if (w.Hour < 12)
{
Console.WriteLine("当前时间:{0}点 上午工作,精神百倍", w.Hour);
}
else
{
// 超过12点,则转入中午工作状态
w.SetState(new NoonState());
w.WriteProgram();
}
}
}中午工作状态:
// 中午工作状态
public class NoonState : State
{
public override void WriteProgram(Work w)
{
if (w.Hour < 13)
{
Console.WriteLine("当前时间:{0}点 饿了,午饭;犯困,午休。", w.Hour);
}
else
{
// 超过13点,则转入下午工作状态
w.SetState(new AfternoonState());
w.WriteProgram();
}
}
}下午工作状态:
// 下午工作状态
public class AfternoonState : State
{
public override void WriteProgram(Work w)
{
if (w.Hour < 17)
{
Console.WriteLine("当前时间:{0}点 下午状态还不错,继续努力", w.Hour);
}
else
{
// 超过17点,则转入傍晚工作状态
w.SetState(new EveningState());
w.WriteProgram();
}
}
}晚间工作状态:
// 晚间工作状态
public class EveningState : State
{
public override void WriteProgram(Work w)
{
if (w.TaskFinished)
{
// 如果完成任务,则转入下班状态
w.SetState(new RestState());
w.WriteProgram();
}
else
{
if (w.Hour < 21)
{
Console.WriteLine("当前时间:{0}点 加班哦,疲累之极", w.Hour);
}
else
{
// 超过21点,则转入睡眠状态
w.SetState(new SleepingState());
w.WriteProgram();
}
}
}
}睡眠状态:
// 睡眠状态
public class SleepingState : State
{
public override void WriteProgram(Work w)
{
Console.WriteLine("当前时间:{0}点 不行了,睡着了。", w.Hour);
}
}下班休息状态:
// 下班休息状态
public class RestState : State
{
public override void WriteProgram(Work w)
{
Console.WriteLine("当前时间:{0}点 下班回家了", w.Hour);
}
}Work类——工作类:
// 工作类
public class Work
{
// 工作初始化为上午工作状态
private State current;
public Work()
{
current = new ForenoonState();
}
// “钟点”属性,状态转换的依据
private double hour;
public double Hour
{
get { return hour; }
set { hour = value; }
}
// “任务完成”属性,是否能下班的依据
private bool finish = false;
public bool TaskFinished
{
get { return finish; }
set { finish = value; }
}
public void SetState(State s)
{
current = s;
}
public void WriteProgram()
{
current.WriteProgram(this);
}
}客户端代码:
static void Main(string[] args)
{
// 紧急项目
Work emergencyProjects = new Work();
emergencyProjects.Hour = 9;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 10;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 12;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 13;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 14;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 17;
// emergencyProjects.TaskFinished = true;
emergencyProjects.TaskFinished = false;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 19;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 22;
emergencyProjects.WriteProgram();
Console.Read();
}运行结果
当前时间:9点 上午工作,精神百倍
当前时间:10点 上午工作,精神百倍
当前时间:12点 饿了,午饭;犯困,午休。
当前时间:13点 下午状态还不错,继续努力
当前时间:14点 下午状态还不错,继续努力
当前时间:17点 加班哦,疲累之极
当前时间:19点 加班哦,疲累之极
当前时间:22点 不行了,睡着了。- 此时的代码,如果要完成‘员工必须在20点之前离开公司’,我们只需要怎么样?增加一个‘强制下班状态’,并改动一下‘傍晚工作状态’类的判断就可以了。而这是不影响其他状态的代码的,符合开放-封闭原则,这样做的确是非常好。
