总目录
前言
状态模式在我们的现实生活中也有类似的例子,例如:在我们上网购买商品的过程中,就可以查看订单的实时状态。对于商家来说,订单的状态不同,也会允许客户有不同的动作要求,比如:订单在已经处于发货状态,此订单是不能退货的。如果订单在备货阶段,客户是可以换货或者退货的。如果我们的订单已经发货了,您就等着接收货物吧,如果货物有质量问题,可以拒签,或者顺利完成交易。
1 基础介绍
- 定义:允许一个对象在其内部状态改变时自动改变其行为,对象看起来就像是改变了它的类。
- 每个对象都有其对应的状态,而每个状态又对应一些相应的行为,如果某个对象有多个状态时,那么就会对应很多的行为。那么对这些状态的判断和根据状态完成的行为,就会导致多重条件语句,并且如果添加一种新的状态时,需要更改之前现有的代码。这样的设计显然违背了开闭原则。状态模式正是用来解决这样的问题的。状态模式将每种状态对应的行为抽象出来成为单独新的对象,这样状态的变化不再依赖于对象内部的行为。
- 状态模式有以下角色:
- 环境角色(Context):也称上下文,定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
- 抽象状态角色(State):定义一个接口,用以封装环境对象的一个特定的状态所对应的行为。
- 具体状态角色(ConcreteState):每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
在状态模式结构中需要理解环境类与抽象状态类的作用:
- 环境类实际上就是拥有状态的对象,环境类有时候可以充当状态管理器(State Manager)的角色,可以在环境类中对状态进行切换操作。
- 抽象状态类可以是抽象类,也可以是接口,不同状态类就是继承这个父类的不同子类,状态类的产生是由于环境类存在多个状态,同时还满足两个条件:这些状态经常需要切换,在不同的状态下对象的行为不同。因此可以将不同对象下的行为单独提取出来封装在具体的状态类中,使得环境类对象在其内部状态改变时可以改变它的行为,对象看起来似乎修改了它的类,而实际上是由于切换到不同的具体状态类实现的。由于环境类可以设置为任一具体状态类,因此它针对抽象状态类进行编程,在程序运行时可以将任一具体状态类的对象设置到环境类中,从而使得环境类可以改变内部状态,并且改变行为。
2 使用场景
-
对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。
-
代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态
3 实现方式
在这里以酒店房间预定为案例说明:
状态变迁:
然后是3个状态类,这个三个状态分别对应:空闲、预订、入住。其中空闲可以完成预订和入住两个动作,预订可以完成入住和退订两个动作,入住可以退房。
状态接口
public interface IState
{
/// <summary>
/// 预定房间
/// </summary>
void bookRoom();
/// <summary>
/// 退订房间
/// </summary>
void UnSubscribeRoom();
/// <summary>
/// 入住
/// </summary>
void CheckInRoom();
/// <summary>
/// 退房
/// </summary>
void CheckOutRoom();
}
房间类(包含状态的环境角色)
public class Room
{
/// <summary>
/// 房间的当前状态
/// </summary>
private IState _state;
public IState State
{
get { return _state; }
set { _state = value; }
}
/// <summary>
/// 房间的三个状态
/// </summary>
private IState _freeTimeState; //空闲状态
private IState _checkInState; //入住状态
private IState _bookedState; //预定状态
/// <summary>
/// 空闲状态
/// </summary>
public IState FreeTimeState
{
get { return _freeTimeState; }
set { _freeTimeState = value; }
}
/// <summary>
/// 入住状态
/// </summary>
public IState CheckInState
{
get { return _checkInState; }
set { _checkInState = value; }
}
/// <summary>
/// 预定状态
/// </summary>
public IState BookedState
{
get { return _bookedState; }
set { _bookedState = value; }
}
public Room()
{
this._freeTimeState = new FreeTimeState(this);
this._checkInState = new CheckInState(this);
this._bookedState = new BookedState(this);
this._state = this._freeTimeState;
}
/// <summary>
/// 预定房间
/// </summary>
public void bookRoom()
{
this._state.bookRoom();
}
/// <summary>
/// 退订房间
/// </summary>
public void UnSubscribeRoom()
{
this._state.UnSubscribeRoom();
}
/// <summary>
/// 入住
/// </summary>
public void CheckInRoom()
{
this._state.CheckInRoom();
}
/// <summary>
/// 退房
/// </summary>
public void CheckOutRoom()
{
this._state.CheckOutRoom();
}
public string getRoomState()
{
return "该房间的状态是:" + this.State.GetType().ToString();
}
}
空闲状态
public class FreeTimeState:IState
{
private Room _room;
public FreeTimeState()
{ }
public FreeTimeState(Room room)
{
this._room= room;
}
public void bookRoom()
{
//设置状态为已经预定状态
this._room.State = this._room.BookedState;
Console.WriteLine("您已经成功预定了...");
}
public void UnSubscribeRoom()
{
//暂不操作
}
public void CheckInRoom()
{
this._room.State = this._room.CheckInState;
Console.WriteLine("您已经成功入住...");
}
public void CheckOutRoom()
{
//暂不操作
}
}
预定状态
public class BookedState:IState
{
private Room _room;
public BookedState()
{ }
public BookedState(Room room)
{
this._room= room;
}
public void bookRoom()
{
Console.WriteLine("该房间已经预定了...");
}
public void UnSubscribeRoom()
{
this._room.State = this._room.FreeTimeState;
Console.WriteLine("退订成功,欢迎下次光临...。");
}
public void CheckInRoom()
{
this._room.State = this._room.CheckInState;
Console.WriteLine("入住成功...");
}
public void CheckOutRoom()
{
//暂不操作
}
}
入住状态
public class CheckInState:IState
{
private Room _room;
public CheckInState()
{ }
public CheckInState(Room room)
{
this._room= room;
}
public void bookRoom()
{
Console.WriteLine("该房间已经入住...");
}
public void UnSubscribeRoom()
{
//暂不操作
}
public void CheckInRoom()
{
Console.WriteLine("该房间已经入住...");
}
public void CheckOutRoom()
{
this._room.State = this._room.FreeTimeState;
Console.WriteLine("退房成功...");
}
}
客户端调用:
static void Main(string[] args)
{
//创建俩间房子
Room[] rooms = new Room[2];
//初始化
for (int i = 0; i < rooms.Length; i++)
{
rooms[i] = new Room();
}
//第一间房子
rooms[0].bookRoom(); //预定
rooms[0].CheckInRoom();//入住
rooms[0].bookRoom(); //再次预定
Console.WriteLine(rooms[0].State);
Console.WriteLine("-------------------");
//第二间房子
rooms[1].CheckInRoom(); //入住
rooms[1].bookRoom(); //预定
rooms[1].CheckInRoom(); //入住
rooms[1].bookRoom();//再次预定
Console.WriteLine(rooms[1].State);
Console.WriteLine("-------------------");
Console.Read();
}
4 优缺点分析
优点:
- 将状态判断逻辑每个状态类里面,可以简化判断的逻辑。
- 当有新的状态出现时,可以通过添加新的状态类来进行扩展,扩展性好。
缺点:
- 如果状态过多的话,会导致有非常多的状态类,加大了开销。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
结语
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
C#设计模式之十八状态模式(State Pattern)【行为型】
C#设计模式(19)——状态者模式(State Pattern)
使用 C# 实现23种常见的设计模式
C#设计模式–状态模式