摘要
本文以 Java 和 C# 示例说明状态模式的概念和应用场景。
定义
状态模式(state pattern)允许对象在内部状态改变时改变它的行为, 对象看起来好像修改了它的类。
解读
- 行为根据状态改变,不同的状态有不同的行为
场景举例
- 自动贩卖机
- 家用电表
- ...
状态模式和策略模式
- 策略模式围绕可以互换的算法进行业务操作
- 状态模式通过改变对象的内部状态来帮助对象控制自己的行为
- 状态模式将各个状态所对应的操作分离开来,即对于不同的状态,由不同的子类实现具体操作,不同状态的切换由子类实现,当发现传入参数不是自己这个状态所对应的参数,则自己给Context类切换状态;而策略模式是直接依赖注入到Context类的参数进行选择策略,不存在切换状态(取自知乎网友的回答)
下面用代码示例进行说明:
代码示例
Java代码
// 状态接口
public interface ManState {
String NextBehavior();
}
// 状态-早上醒来
public class Wakeup implements ManState {
@Override
public String NextBehavior() {
return "正在起床";
}
}
// 状态-吃早餐
public class Eatting implements ManState {
@Override
public String NextBehavior() {
return "正在吃早饭";
}
}
// 状态-上班
public class Working implements ManState{
@Override
public String NextBehavior() {
return "正在工作";
}
}
// 状态-睡觉
public class Sleeping implements ManState{
@Override
public String NextBehavior() {
return "正在睡觉";
}
}
// 上下文 - Man
public class Man {
private ManState state;
public Man(){
state = null;
}
// 设置状态
public void setState(ManState state){
this.state = state;
}
// 获取状态
public ManState getState(){
return state;
}
}
public static void main(String[] args) throws Exception {
//定义上下文
Man man = new Man();
// 醒了
ManState state = new Wakeup();
this.man.setState(state);
this.man.getState().NextBehavior();
// 吃饭
state = new Eatting();
this.man.setState(state);
this.man.getState().NextBehavior();
// 上班
state = new Working();
this.man.setState(state);
this.man.getState().NextBehavior();
//睡觉
state = new Sleeping();
this.man.setState(state);
this.man.getState().NextBehavior();
}
上面的例子,以员工为例,说明一天中做的事情,其状态是在外部进行控制的,事实上,根据需求,也可以在内部实现状态的转换,这就需要将上下文注入到具体状态的实现中,以实现一个类似指针的效果。
public interface EmpState {
void DoSomething(Employee emp);
}
public class Wakeup implements EmpState {
@Override
public void DoSomething(Employee emp) {
System.out.println("起床了");
emp.SetState(new Eatting());
}
}
public class Eatting implements EmpState {
@Override
public void DoSomething(Employee emp) {
System.out.println("吃早饭呢");
emp.SetState(new Working());
}
}
public class Working implements EmpState {
@Override
public void DoSomething(Employee emp) {
System.out.println("正在工作");
emp.SetState(new Sleeping());
}
}
public class Sleeping implements EmpState {
@Override
public void DoSomething(Employee emp) {
System.out.println("正在睡觉");
System.out.println("没有其他状态了");
}
}
public class JavaStateDemo {
public static void main(String[] args) {
EmpState state = new Wakeup();
//需要一个初始状态
Employee emp = new Employee(state);
emp.Go();
emp.Go();
emp.Go();
emp.Go();
}
}
这个例子从内部改变Employee的状态,以实现不同的行为。
员工请假/工作天数计算示例
设想有以下场景:需求方连续请假和非连续请假的处理措施是不同的,需要清楚的知道员工每个月的工作和请假情况,我们实现如下的状态操作模型
using System;
namespace StatePatternDemo
{
// 抽象状态接口
public interface IWorkState
{
void HandleWorkState(Employee emp);
}
// 请假
public class Leaving:IWorkState
{
public void HandleWorkState(Employee emp)
{
System.Console.WriteLine($"{emp.Name} 请假 {emp.LeavingDays} 天");
System.Console.WriteLine($"{emp.Name} 上次请假日期是 {emp.LastLeavingDate} ");
}
}
// 工作
public class Working:IWorkState
{
public void HandleWorkState(Employee emp)
{
System.Console.WriteLine($"{emp.Name} 正常上班中...");
System.Console.WriteLine($"{emp.Name} 已连续工作 {Datetime.Now.Day - LastLeavingDate} 天");
}
}
// 上下文
public class Employee
{
// 职员姓名(标识)
public string Name { get; set; }
public double LeavingDays { get; set; } // 本次请假天数
public Datetime LastLeavingDate { get; set; } // 上次请假日期
public double TotalLeavingDays { get; set; } // 共请假天数
// 工作天数
public double WorkingDays { get; set; }
//公休天数
private int restDays { get; set;}
private IWorkState WorkState;
public Employee(IWorkState workState)
{
this.WorkState = workState;
}
public void SetState(IWorkState workState)
{
this.WorkState = workState;
}
///
/// 上班签到或请假导致的状态改变
///
public void WorkingStateChanging(Employee emp)
{
this.WorkState.HandleWorkState(Employee emp);
}
}
}
如上代码中,我把上下文类变成了Employee对象,代表一个职员,这个职员有两种不同的状态,其中请假状态还可以细分为连续请假和非连续请假,也就是说,如果代码继续细化,我们还可以在该状态模式中再嵌套一个状态模式,以更清楚的描述需求。
namespace StatePatternDemo
{
public interface ILeavingState
{
void Leaving(Employee emp);
}
public class NotContinusLeaving:ILeavingState
{
public void Leaving(Employee emp)
{
//非连续请假的实现逻辑
}
}
public class ContinusLeaving:ILeavingState
{
public void Leaving(Employee emp)
{
//非连续请假的实现逻辑
}
}
// 其他逻辑
}
小结:
- 状态模式避免了代码中繁复的if/else/switch逻辑,使代码更容易阅读和维护
- 将状态引起的变化独立封装在各自的实现中,所以它符合开闭原则
以上就是我对状态模式的理解,认为我理解的有问题的,可以在下面留言,谢谢大家!