状态模式
本篇博客将介绍状态模式,状态模式是一种较为复杂的设计模式,用于解决系统中复杂对象的状态转换以及不同状态下的封装问题。当一个系统中的某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下会存在不同的行为时可以使用状态模式。
模式分类
行为型设计模式。
模式产生的原因
在现实生活中的水存在多种状态,他可以凝固成冰,也可以蒸发为水蒸气。在软件系统中,有些对象也会像水一样有多种状态,这些状态-在某种状态下可以相互转换,而对象在不同的状态下也将会有不同的行为,一般我们可以使用复杂的分支语句来进行状态的判断和转换操作,但这将会导致代码的可维护性和灵活性下降,特别是出现新的状态时,代码的拓展性也很差,客户端的代码写需要进行相应的修改,违背了开闭原则,为了解决这个问题,我们可以使用状态模式。状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为而得封装问题。
模式类图
经典状态模式由3个对象构成:
Context(环境类 / 状态机类):
环境类是拥有多个状态的对象由于环境类的状态存在多样性,且在不同状态下的对象的行为有所不同,所以将状态独立出去形成独立的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义为当前状态,在具体实现时,它是一个State子类的对象。
State(状态类):
它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现了这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写现在抽象类中。
ConcreteState(具体状态里):
他是抽象状态类的子类,每一个具体状态类实现一个与环境类的一个状态相关的行为,对应环境类中的一个具体状态。
事实上,经典状态模式只是提供了一个大体上的思路,在实际使用上并不能算是有多么好用,所以我们将状态模式优化,下面的类图是在使用Unity做人物框架时修改后的状态模式,和经典模式最大的区别在于这里我们将条件与状态分离:
代码实现
这里我们使用优化后的状态模式在书写例子:
某软件公司要为一个银行开发一套信用卡业务系统,银行账户(Account)是该系统的核心类之一,通过分析,该软件公司开发人员发现在系统中账户存在3个状态,且在不同的状态下账户存在不同的行为。具体说明如下:
- 如果账户中余额大于等于0,则用户处于正常状态(Normal State),此时用户可以存款也可以取款。
- 如果账户中余额小于0,并且大于-2000,用户处于透支状态,此时用户可以存款也可以取款,但需要按天计算利息。
- 如果账户中余额小于-2000,,此时用户处于受限状态,此时用户只能存款,且需要按天计算利息。
现在使用状态模式设计这个系统。
Account类:
using System;
using System.Collections.Generic;
using State.State.Example.MyState;
namespace State.State.Example
{
public abstract class Account
{
public int CurrentMoney;
private Dictionary<StateId, AccountState> _states = new Dictionary<StateId, AccountState>();
protected AccountState _accountCurrentState;
public Account()
{
InitState();
}
public abstract void InitState();
public void Add(AccountState state)
{
_states.Add(state.Id, state);
}
public void Remove(StateId id)
{
_states.Remove(id);
}
public AccountState Find(StateId id)
{
return _states[id];
}
public void SetState(AccountState state, AccountArgs args)
{
_accountCurrentState?.OnStateExit(args);
_accountCurrentState = state;
_accountCurrentState.OnStateEnter(args);
}
public virtual void Deposit(AccountArgs args)
{
SubDeposit(args);
_accountCurrentState.StateCheck(args);
}
public abstract void SubDeposit(AccountArgs args);
public virtual void Withdrawal(AccountArgs args)
{
SubWithdrawal(args);
_accountCurrentState.StateCheck(args);
}
public abstract void SubWithdrawal(AccountArgs args);
}
}
Account参数类:
namespace State.State.Example
{
public class AccountArgs
{
public int CurrentAccountMoney;
public int Money;
}
}
具体用户类:
using System;
using State.State.Example.MyState;
using State.State.Example.MyTrigger;
namespace State.State.Example
{
public class JohAccount : Account
{
public override void InitState()
{
NormalState normalState = new NormalState(this);
Add(normalState);
normalState.Add(TriggerId.NormalToOverdraft, StateId.Overdraft);
normalState.Add(TriggerId.NormalToRestricted, StateId.Restricted);
OverdraftState overdraftState = new OverdraftState(this);
Add(overdraftState);
overdraftState.Add(TriggerId.OverdraftToNormal, StateId.Normal);
overdraftState.Add(TriggerId.OverdraftToRestricted, StateId.Restricted);
RestrictedState restrictedState = new RestrictedState(this);
Add(restrictedState);
restrictedState.Add(TriggerId.RestrictedToNormal, StateId.Normal);
restrictedState.Add(TriggerId.RestrictedToOverdraft, StateId.Overdraft);
SetState(normalState, new AccountArgs()
{
CurrentAccountMoney = 0,
Money = 0,
});
}
public override void SubDeposit(AccountArgs args)
{
Console.WriteLine($"存钱:{args.Money}");
}
public override void SubWithdrawal(AccountArgs args)
{
Console.WriteLine($"取钱:{args.Money}");
args.Money = -args.Money;
}
}
}
抽象工厂类:
using System;
namespace State.State.Example.MyFactory
{
public abstract class Factory<T>
{
public abstract T Create(Enum idEnum);
}
}
状态工厂类:
using System;
using State.State.Example.MyState;
namespace State.State.Example.MyFactory
{
public class StateFactory : Factory<AccountState>
{
public override AccountState Create(Enum idEnum)
{
Type type = Type.GetType($"State.State.Example.MyTrigger.{idEnum}");
if (type == null)
{
return null;
}
return Activator.CreateInstance(type) as AccountState;
}
}
}
条件工厂类:
using System;
using State.State.Example.MyTrigger;
namespace State.State.Example.MyFactory
{
public class TriggerFactory : Factory<Trigger>
{
public override Trigger Create(Enum idEnum)
{
Type type = Type.GetType($"State.State.Example.MyTrigger.{idEnum}");
if (type == null)
{
return null;
}
Trigger trigger = Activator.CreateInstance(type) as Trigger;
return trigger;
}
}
}
账户状态基类:
using System;
using System.Collections.Generic;
using State.State.Example.MyFactory;
using State.State.Example.MyTrigger;
namespace State.State.Example.MyState
{
public abstract class AccountState
{
public StateId Id;
private Account _currentAccount;
private Dictionary<TriggerId, StateId> _stateMap = new Dictionary<TriggerId, StateId>();
private List<Trigger> _triggers = new List<Trigger>();
private TriggerFactory _factory = new TriggerFactory();
public AccountState(Account account)
{
_currentAccount = account;
InitId();
}
public abstract void InitId();
public void Add(TriggerId trigger, StateId state)
{
_stateMap.Add(trigger, state);
Trigger myTrigger = _factory.Create(trigger);
_triggers.Add(myTrigger);
}
public void Remove(TriggerId trigger)
{
_stateMap.Remove(trigger);
}
public virtual void OnStateEnter(AccountArgs args)
{
Console.WriteLine($"当前状态为:{Id}");
_currentAccount.CurrentMoney = args.CurrentAccountMoney + args.Money;
Console.WriteLine($"当前账户余额为:{_currentAccount.CurrentMoney}");
}
public virtual void OnStateExit(AccountArgs args)
{
}
protected virtual void OnStateStay(AccountArgs args)
{
Console.WriteLine($"当前状态为:{Id}");
_currentAccount.CurrentMoney = args.CurrentAccountMoney + args.Money;
Console.WriteLine($"当前账户余额为:{_currentAccount.CurrentMoney}");
}
public void StateCheck(AccountArgs args)
{
foreach (var item in _triggers)
{
if (item.Check(args))
{
StateId id = _stateMap[item.Id];
_currentAccount.SetState(_currentAccount.Find(id), args);
return;
}
}
OnStateStay(args);
}
}
}
正常状态:
using System;
namespace State.State.Example.MyState
{
public class NormalState : AccountState
{
public NormalState(Account account) : base(account)
{
}
public override void InitId()
{
Id = StateId.Normal;
}
}
}
透支状态:
namespace State.State.Example.MyState
{
public class OverdraftState : AccountState
{
public OverdraftState(Account account) : base(account)
{
}
public override void InitId()
{
Id = StateId.Overdraft;
}
}
}
限制状态:
namespace State.State.Example.MyState
{
public class RestrictedState : AccountState
{
public RestrictedState(Account account) : base(account)
{
}
public override void InitId()
{
Id = StateId.Restricted;
}
}
}
状态ID:
namespace State.State.Example.MyState
{
public enum StateId
{
Normal,
Overdraft,
Restricted,
}
}
条件ID:
namespace State.State.Example.MyTrigger
{
public enum TriggerId
{
NormalToOverdraft,
NormalToRestricted,
OverdraftToNormal,
OverdraftToRestricted,
RestrictedToNormal,
RestrictedToOverdraft,
}
}
条件基类:
namespace State.State.Example.MyTrigger
{
public abstract class Trigger
{
public TriggerId Id;
public Trigger()
{
InitId();
}
public abstract void InitId();
public abstract bool Check(AccountArgs args);
}
}
条件类:
namespace State.State.Example.MyTrigger
{
public class NormalToOverdraft : Trigger
{
public override void InitId()
{
Id = TriggerId.NormalToOverdraft;
}
public override bool Check(AccountArgs args)
{
int money = args.CurrentAccountMoney + args.Money;
if (money > -2000 && money < 0)
{
return true;
}
return false;
}
}
}
namespace State.State.Example.MyTrigger
{
public class NormalToRestricted : Trigger
{
public override void InitId()
{
Id = TriggerId.NormalToRestricted;
}
public override bool Check(AccountArgs args)
{
int money = args.CurrentAccountMoney + args.Money;
if (money <= -2000)
{
return true;
}
return false;
}
}
}
namespace State.State.Example.MyTrigger
{
public class OverdraftToNormal : Trigger
{
public override void InitId()
{
Id = TriggerId.OverdraftToNormal;
}
public override bool Check(AccountArgs args)
{
int money = args.CurrentAccountMoney + args.Money;
if (money >= 0)
{
return true;
}
return false;
}
}
}
namespace State.State.Example.MyTrigger
{
public class OverdraftToRestricted : Trigger
{
public override void InitId()
{
Id = TriggerId.OverdraftToRestricted;
}
public override bool Check(AccountArgs args)
{
int money = args.CurrentAccountMoney + args.Money;
if (money <= -2000)
{
return true;
}
return false;
}
}
}
namespace State.State.Example.MyTrigger
{
public class RestrictedToNormal : Trigger
{
public override void InitId()
{
Id = TriggerId.RestrictedToNormal;
}
public override bool Check(AccountArgs args)
{
int money = args.CurrentAccountMoney + args.Money;
if (money >= 0)
{
return true;
}
return false;
}
}
}
namespace State.State.Example.MyTrigger
{
public class RestrictedToOverdraft : Trigger
{
public override void InitId()
{
Id = TriggerId.RestrictedToOverdraft;
}
public override bool Check(AccountArgs args)
{
int money = args.CurrentAccountMoney + args.Money;
if (money > -2000 && money < 0)
{
return true;
}
return false;
}
}
}
Program类:
using State.State.Example;
namespace State
{
internal class Program
{
public static void Main(string[] args)
{
JohAccount johAccount = new JohAccount();
johAccount.Deposit(new AccountArgs()
{
CurrentAccountMoney = johAccount.CurrentMoney,
Money = 1000,
});
johAccount.Withdrawal(new AccountArgs()
{
CurrentAccountMoney = johAccount.CurrentMoney,
Money = 2000,
});
johAccount.Deposit(new AccountArgs()
{
CurrentAccountMoney = johAccount.CurrentMoney,
Money = 3000,
});
johAccount.Withdrawal(new AccountArgs()
{
CurrentAccountMoney = johAccount.CurrentMoney,
Money = 4000,
});
johAccount.Withdrawal(new AccountArgs()
{
CurrentAccountMoney = johAccount.CurrentMoney,
Money = 1000,
});
}
}
}
状态模式的总结
状态模式的优点:
- 状态模式将所有与某个状态相关的行为放在一个类中,只需要注入不同的状态就可以拥有一个不同的行为,符合开闭原则。
状态模式的缺点:
- 状态模式将会增加系统中类的个数,导致系统的运行开销增加。
- 其设计较为复杂,增加系统设计难度。