状态者模式的介绍
每个对象都有其对应的状态,而每个状态又对应一些相应的行为,如果某个对象有多个状态时,那么就会对应很多的行为。那么对这些状态的判断和根据状态完成的行为,就会导致多重条件语句,并且如果添加一种新的状态时,需要更改之前现有的代码。这样的设计显然违背了开闭原则。状态模式正是用来解决这样的问题的。状态模式将每种状态对应的行为抽象出来成为单独新的对象,这样状态的变化不再依赖于对象内部的行为。
状态者模式的定义
上面对状态模式做了一个简单的介绍,这里给出状态模式的定义。
状态模式——允许一个对象在其内部状态改变时自动改变其行为,对象看起来就像是改变了它的类。
状态者模式的结构
既然状态者模式是对已有对象的状态进行抽象,则自然就有抽象状态者类和具体状态者类,而原来已有对象需要保存抽象状态者类的引用,通过调用抽象状态者的行为来改变已有对象的行为。经过上面的分析,状态者模式的结构图也就很容易理解了,具体结构图如下图示。
- State----抽象状态角色
- ConcreteState----具体状态角色
- Context----环境角色
- 把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。
- 环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。
namespace ConsoleApplication1{ ////// 抽象状态角色 /// public abstract class State { //定义一个环境角色,提供子类访问 protected Context context; public Context Context { set { context = value; } } //行为1 public abstract void handle1(); //行为2 public abstract void handle2(); } ////// 具体状态角色 /// public class ConcreteState1 : State { public override void handle1() { //本状态下必须处理的逻辑 } public override void handle2() { //设置当前状态为state2 context.CurrentState = Context.STATE2; //过渡到state2状态,由Context实现 context.handle2(); } } ////// 具体状态角色 /// public class ConcreteState2 : State { public override void handle1() { //设置当前状态为state1 context.CurrentState = Context.STATE1; //过渡到state1状态,由Context实现 context.handle1(); } public override void handle2() { //本状态下必须处理的逻辑 } } ////// 具体环境角色 /// public class Context { //定义状态 public static State STATE1 = new ConcreteState1(); public static State STATE2 = new ConcreteState2(); //当前状态 private State currentState; public State CurrentState { get { //获取当前状态 return currentState; } set { //设置当前状态 currentState=value; currentState.Context=this; } } //行为委托 public void handle1() { currentState.handle1(); } public void handle2() { currentState.handle2(); } } class Program { static void Main(string[] args) { //定义环境角色 Context context = new Context(); //初始化状态 context.CurrentState = new ConcreteState1(); //行为执行 context.handle1(); context.handle2(); Console.ReadLine(); } }}
状态者模式的优缺点
状态者模式的主要优点是:
- 结构清晰
- 遵循设计原则
- 封装性非常好
状态者模式的主要缺点是:
- 子类会太多,也就是类膨胀。
状态者模式的应用场景
在以下情况下可以考虑使用状态者模式。
- 行为随状态改变而改变的场景
- 条件、分组判断语句的替代者
应用一:
用状态模式来模拟一个电梯的状态场景:
具体代码实现:
namespace ConsoleApplication1{ ////// 抽象电梯状态 /// public abstract class LiftState { protected Context context; public Context Context { set { context = value; } } //首先电梯们开启动作 public abstract void open(); //电梯们有开启,那当然也就有关闭了 public abstract void close(); //电梯要能上能下,运行起来 public abstract void run(); //电梯还要能停下来 public abstract void stop(); } ////// 敞门状态 /// public class OpenningState : LiftState { //打开电梯门 public override void open() { Console.WriteLine("电梯门开启..."); } public override void close() { context.LiftState = Context.closeingState; context.LiftState.close(); } public override void run() { } public override void stop() { } } ////// 关闭状态 /// public class CloseingState : LiftState { //电梯门关了再打开 public override void open() { //设置为敞门状态 context.LiftState = Context.openningState; context.LiftState.open(); } //电梯门关闭,这是关闭状态要实现的动作 public override void close() { Console.WriteLine("电梯门关闭..."); } //电梯门关了就运行 public override void run() { //设置为运行状态 context.LiftState = Context.runingState; context.LiftState.run(); } //电梯门关着 public override void stop() { //设置为停止状态 context.LiftState = Context.stoppingState; context.LiftState.stop(); } } ////// 运行状态 /// public class RunningSate : LiftState { public override void open() { } public override void close() { } public override void run() { Console.WriteLine("电梯上下运行..."); } public override void stop() { //设置为停止状态 context.LiftState = Context.stoppingState; context.LiftState.stop(); } } ////// 停止状态 /// public class StoppingState : LiftState { public override void open() { //设置为敞门状态 context.LiftState = Context.openningState; context.LiftState.open(); } public override void close() { } public override void run() { //设置为运行状态 context.LiftState = Context.runingState; context.LiftState.run(); } public override void stop() { Console.WriteLine("电梯停止了..."); } } ////// 上下文类 /// public class Context { //定义出所有的电梯状态 public static OpenningState openningState = new OpenningState(); public static CloseingState closeingState = new CloseingState(); public static RunningSate runingState = new RunningSate(); public static StoppingState stoppingState = new StoppingState(); //定义一个当前电梯状态 private LiftState liftState; public LiftState LiftState { get { return liftState; } set { liftState = value; //把当前的环境通知到各个实现类中 liftState.Context = this; } } public void open() { liftState.open(); } public void close() { liftState.close(); } public void run() { liftState.run(); } public void stop() { liftState.stop(); } } class Program { static void Main(string[] args) { Context context = new Context(); context.LiftState = new CloseingState(); context.open(); context.close(); context.run(); context.stop(); Console.ReadLine(); } }}
应用二:
下面,就以银行账户的状态来实现下状态者模式。银行账户根据余额可分为RedState、SilverState和GoldState。这些状态分别代表透支账号,新开账户和标准账户。账号余额在【-100.0,0.0】范围表示处于RedState状态,账号余额在【0.0 , 1000.0】范围表示处于SilverState,账号在【1000.0, 100000.0】范围表示处于GoldState状态。下面以这样的一个场景实现下状态者模式,具体实现代码如下所示:
namespace ConsoleApplication1{ ////// 抽象存款状态 /// public abstract class State { protected Account context; public Account Context { set { context = value; } } ////// 余额 /// /// 利率 /// public double Interest { get; set; } ////// 下限 /// public double LowerLimit { get; set; } ////// 上限 /// public double UpperLimit { get; set; } public abstract void Deposit(double amount); // 存款 public abstract void Withdraw(double amount); // 取钱 public abstract void PayInterest(); // 获得的利息 public void Display(string OperationType, double amount) { switch (OperationType) { case "Deposit": Console.WriteLine("存款金额为 {0:C}——", amount); Console.WriteLine("账户余额为 =:{0:C}", this.Balance); Console.WriteLine("账户状态为: {0}", this.GetType().Name); Console.WriteLine(); break; case "Withdraw": Console.WriteLine("取款金额为 {0:C}——", amount); Console.WriteLine("账户余额为 =:{0:C}", this.Balance); Console.WriteLine("账户状态为: {0}", this.GetType().Name); Console.WriteLine(); break; case "PayInterest": Console.WriteLine("Interest Paid --- "); Console.WriteLine("账户余额为 =:{0:C}", this.Balance); Console.WriteLine("账户状态为: {0}", this.GetType().Name); Console.WriteLine(); break; default: break; } } } ////// Red State意味着Account透支 /// public class RedState : State { public RedState() { Interest = 0.00; LowerLimit = -100.00; UpperLimit = 0.00; } public RedState(double balance) :this() { this.Balance = balance; } // 存款 public override void Deposit(double amount) { Balance += amount; if (Balance > UpperLimit) { context.AccountState = new SilverState(this.Balance); //Context.silverState; } } // 取钱 public override void Withdraw(double amount) { Console.WriteLine("没有钱可以取了!"); } public override void PayInterest() { // 没有利息 } } ////// Silver State意味着没有利息得 /// public class SilverState : State { public SilverState() { Interest = 0.00; LowerLimit = 0.00; UpperLimit = 1000.00; } public SilverState(double balance) :this() { this.Balance = balance; } public override void Deposit(double amount) { Balance += amount; StateChangeCheck(); } public override void Withdraw(double amount) { Balance -= amount; StateChangeCheck(); } public override void PayInterest() { Balance += Interest * Balance; StateChangeCheck(); } private void StateChangeCheck() { if (Balance < LowerLimit) { context.AccountState = new RedState(this.Balance);//Context.redState; } else if (Balance > UpperLimit) { context.AccountState = new GoldState(this.Balance);//Context.goldState; } } } ////// Gold State意味着有利息状态 /// public class GoldState : State { public GoldState() { Interest = 0.05; LowerLimit = 1000.00; UpperLimit = 1000000.00; } public GoldState(double balance) :this() { this.Balance = balance; } public override void Deposit(double amount) { Balance += amount; StateChangeCheck(); } public override void Withdraw(double amount) { Balance -= amount; StateChangeCheck(); } public override void PayInterest() { Balance += Interest * Balance; StateChangeCheck(); } private void StateChangeCheck() { if (Balance < 0.0) { context.AccountState = new RedState(this.Balance);//Context.redState; } else if (Balance < LowerLimit) { context.AccountState = new SilverState(this.Balance);//Context.silverState; } } } ////// 上下文类 /// public class Account { //定义出所有的存款账户状态 //public static RedState redState = new RedState(); //public static SilverState silverState = new SilverState(); //public static GoldState goldState = new GoldState(); //定义一个当前存款账户状态 private State accountState; public State AccountState { get { return accountState; } set { accountState = value; //把当前的环境通知到各个实现类中 accountState.Context = this; } } public void Deposit(double amount) { accountState.Deposit(amount); accountState.Display("Deposit", amount); } public void Withdraw(double amount) { accountState.Withdraw(amount); accountState.Display("Withdraw", amount); } public void PayInterest() { accountState.PayInterest(); accountState.Display("PayInterest", 0); } } class Program { static void Main(string[] args) { Account account = new Account(); account.AccountState = new SilverState(0.0); // 进行交易 // 存钱 account.Deposit(1000.0); account.Deposit(200.0); account.Deposit(600.0); // 付利息 account.PayInterest(); // 取钱 account.Withdraw(2000.00); account.Withdraw(500.00); Console.ReadLine(); } }}