什么是状态模式
状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。
状态模式应用场景
1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2.操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。 通常,有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
案例:
作为一辆车它的状态其实很简单,也就是运行、加速、减速、停车状态。在停车状态,我们可以控制它启动运行,切换至运行状态;在运行状态,自动驾驶根据行驶条件控制加速减速,切换至加速减速状态;到达目的地后,泊车切换到停车状态。
状态接口:
/// <summary> /// 状态接口类 /// </summary> public interface ICarState { /// <summary> /// 启动 /// </summary> void Drive(Car car); /// <summary> /// 停车 /// </summary> void Stop(Car car); /// <summary> /// 加速 /// </summary> /// <param name="car"></param> void SpeedUp(Car car); /// <summary> /// 减速 /// </summary> /// <param name="car"></param> void SpeedDown(Car car); }
然后我们依次实现这四种状态。
运行状态下可以切换到其他三种状态。
/// <summary> /// 运行状态 /// </summary> public class RuningState : ICarState { public void Drive(Car car) { Console.WriteLine("车辆正在自动驾驶!"); } public void Stop(Car car) { Console.WriteLine("车辆已停止!"); car.CurrentCarState = Car.StopState; } public void SpeedUp(Car car) { Console.WriteLine("路况良好,开始加速行驶!"); car.CurrentCarState = Car.SpeedUpState; } public void SpeedDown(Car car) { Console.WriteLine("路况一般,开始加速行驶!"); car.CurrentCarState = Car.SpeedDownState; } }
停车状态下,只能切换到启动状态,不可加速减速。
/// <summary> /// 停车状态 /// </summary> public class StopState : ICarState { public void Drive(Car car) { Console.WriteLine($"{car.Name}已启动,开始自动驾驶!"); car.CurrentCarState = Car.RunState; } public void Stop(Car car) { Console.WriteLine("车辆已停止!"); } public void SpeedUp(Car car) { Console.WriteLine("车辆已停止!"); } public void SpeedDown(Car car) { Console.WriteLine("车辆已停止!"); } }
/// <summary> /// 加速状态 /// </summary> public class SpeedUpState : ICarState { public void Drive(Car car) { Console.WriteLine("车辆正在自动驾驶!"); } public void Stop(Car car) { Console.WriteLine("车辆已停止!"); car.CurrentCarState = Car.StopState; } public void SpeedUp(Car car) { Console.WriteLine("车辆正在加速行驶!"); } public void SpeedDown(Car car) { Console.WriteLine("路况一般,减速行驶!"); car.CurrentCarState = Car.SpeedDownState; } }
/// <summary> /// 减速状态 /// </summary> public class SpeedDownState : ICarState { public void Drive(Car car) { Console.WriteLine("车辆正在自动驾驶!"); } public void Stop(Car car) { Console.WriteLine("车辆已停止!"); car.CurrentCarState = Car.StopState; } public void SpeedUp(Car car) { Console.WriteLine("路况良好,加速行驶!"); car.CurrentCarState = Car.SpeedUpState; } public void SpeedDown(Car car) { Console.WriteLine("车辆正在减速行驶!"); } }
定义完状态,下面我们就来看看实际的车类。
public class Car { public string Name { get; set; } public Car() { this.CurrentCarState = StopState;//初始状态为停车状态 } internal static ICarState StopState = new StopState(); internal static ICarState RunState = new RuningState(); internal static ICarState SpeedDownState = new SpeedDownState(); internal static ICarState SpeedUpState = new SpeedUpState(); public ICarState CurrentCarState { get; set; } public void Run() { this.CurrentCarState.Drive(this); } public void Stop() { this.CurrentCarState.Stop(this); } public void SpeedUp() { this.CurrentCarState.SpeedUp(this); } public void SpeedDown() { this.CurrentCarState.SpeedDown(this); } }
Car类也比较简单,主要是预先申明并实例化了几种状态并暴露设置当前状态的属性,以及提供了状态对应的行为方法,并委托给具体的状态去执行相应的动作。
下面就是简单的场景类了。
static void Main(string[] args) { Car tesla = new Car() {Name = "特斯拉 Model S"}; tesla.Run(); tesla.SpeedUp(); tesla.SpeedDown(); tesla.Stop(); Console.WriteLine(); }
总结:
状态模式隐藏了具体的状态变化,但行为还是由状态变化驱动的。
就状态模式而言,其实就仅仅三个角色:
- State——抽象状态角色:接口或抽象类,负责定义对象的所有状态对应的行为由具体状态去实现。这里对应的是我们定义的
ICarState
。 - ConcreteState——具体状态角色:处理当前状态的行为,决定是否可以过渡到其他状态。这里对应的是我们定义的
RunState
、StopState
、SepeedUpState
、SepeedDownState
。 - Context——环境角色:定义行为,状态转换。这里对应的就是我们的
Car
类。
优缺点:
优点:结构清晰;符合OCP和SRP;封装性好。
缺点 :在状态过多的情况下,会导致具体状态类的膨胀。
应用场景:
- 行为随状态改变而改变。
- 状态已确定,且状态不宜过多。
- 重构
if..else
或switch..case
的不二之选。