状态模式类图:
Context的request方法,根据自身的状态属性State, 执行state.handle方法,根据面向对象的多态原则,在运行时,由于可以动态修改Context的状态state,从而ConcreteState转化成另一个ConctreteState,执行各自的业务逻辑。
我总结状态模式要注意以下几点:
1. 在状态模式中,Context是持有状态的对象State,并可以修改状态setState.
2. 但是Context并不处理跟状态相关的行为,而是把处理状态的功能委托给了ConctreteState类来处理。
3. 在ConctreteState类中除了处理它的业务外,还要根据业务来修改Context的State,以达到修改状态的目的。(如果在Context中修改state就会有大量的if else)
4. 客户端一般只和Context交互。客户端通常不负责运行期间状态的维护,也不负责决定后续到底使用哪一个具体的状态处理对象。
来看一下代码:
假设银行账户的密码输入错误三次,账户状态改为Forbidden,如果输入错误6点,则locked.
public class Account { private readonly string password = "123456"; private int tryCount = 0; public Account() { // 账启初始化时为,normal状态 this.State = new Normal(this); } public AccountState State { get; set; } public void Validate(string pwd) { if (pwd != password) { tryCount++; State.Show(tryCount); } } } public abstract class AccountState { protected Account account; protected int maxTryCount; public abstract void Show(int tryCount); } public class Normal : AccountState { public Normal(Account account) { this.account = account; maxTryCount = 2; } public override void Show(int tryCount) { Console.WriteLine("Account State is Normal"); StateChange(tryCount); } private void StateChange(int tryCount) { if (tryCount >= maxTryCount) { account.State = new Forbidden(this.account); } } } public class Forbidden : AccountState { public Forbidden(Account account) { this.account = account; maxTryCount = 5; } public override void Show(int tryCount) { Console.WriteLine("Account State is Forbidden"); StateChange(tryCount); } private void StateChange(int tryCount) { if (tryCount >= maxTryCount) { account.State = new Locked(account); } } } public class Locked : AccountState { public Locked(Account account) { } public override void Show(int tryCount) { Console.WriteLine("Account State is Locked"); } } class Program { static void Main(string[] args) { Account account = new Account(); for (int i = 0; i < 6; i++) { account.Validate("111111"); } Console.ReadKey(); } }
个人的总结:
1. 单纯的依靠状态模式没有很好的if else的问题,而且会产生大量的子类。
2. 子类之间的相互耦合,有点不能让人接受,哪果业务有变化:输入错误10次,就报警,那不仅要加一个类,而且还要修改locked类,没有做到开闭原则。
当然可以把状态的修改放在context里,通过 if else方式来处理,这样就可以做到开闭原则,但是又回到1的问题了。