设计模式--State状态模式
所谓状态模式是指:当一个对象的内部状态改变时,它的行为也随之改变,如何解藕状态与行为之间的藕合关系,且避免大量的if….else…语句的出现,从而使得“对象看起来似乎修改了它的类”。
状态模式的实现非常的简单,就是将状态抽象出来,然后在状态中处理这些请求,这也是面向对象抽象特征的基本应用。
状态模式的UML图如下:
相关角色:
1、状态属主角色Context: 就是状态所描述的事物,如一只狗的状态为睡,站,走,跑,叫....,那么我们所定义的一个类"Dog"就是这里的Context。此角色要定义一些外部关注的接口或服务功能等,同时还要维护一个具体状态角色的实例对象。
2、抽象状态角色State:它是抽象类或接口,是具体状态角色的基类,它定义了具体状态角色所共有的行为并进行封装,由具体状态角色予以不同的实现。
3、具体状态角色 Concrete State:继承自抽象状态角色,并具体实现抽象状态角色定义的接口功能,同时还提供了不同具体状态角色间的切换判断以根据状态属主角色对象的某些条件来决定何时以及如何进行状态的切换。
下面我们用代码来示例State状态模式。
程序如下图:
一、状态模式基本思路示例
1、状态属主角色Context:
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyStatePattern
{
class Context
{
#region State属性
private State _state;
public State State
{
get { return _state; }
set
{
_state = value;
Console.WriteLine("State:{0}", _state.GetType().Name);
}
}
#endregion
#region 构造函数
public Context(State state)
{
this._state = state;
}
#endregion
#region 服务请求
public void Request()
{
_state.Handle(this);
}
#endregion
}
}
2、抽象状态角色State:
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyStatePattern
{
#region 抽象状态角色
abstract class State
{
public abstract void Handle(Context context);
}
#endregion
}
3、具体状态角色Concrete State:
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyStatePattern
{
//定义具体状态角色
#region 具体状态角色A
class ConcreteStateA:State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateB();
}
}
#endregion
#region 具体状态角色B
class ConcreteStateB : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateA();
}
}
#endregion
}
4、客户应用:
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
#region 基本思路示例
Console.WriteLine("-------------基本思路示例--------------");
Context c = new Context(new ConcreteStateA());
c.Request();
c.Request();
c.Request();
c.Request();
Console.ReadKey();
#endregion
二、俱乐部会员服务的状态模式实现
定义一个俱乐部,它的顾客有三个状态:一般访客,普通会员,贵宾会员。每个顾客都有一个帐户,帐户上存放顾客的存款,根据存款的不同数量切换会员的级别, 当顾客提请俱乐部的服务时,俱乐部将根据顾客的当前级别提供与其级别对应的服务内容。当然,顾客可以存入或消费,并及时修改他们的帐户。
1、状态属主角色Context:ClubAccount
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyStatePattern
{
class ClubAccount
{
#region 帐户姓名属性
private string _customerName;
public string CustomerName
{
get { return _customerName; }
set { _customerName = value; }
}
#endregion
#region 帐户状态State属性
//ClubAccount类中包含了State类对象实例,而正是因为State类的具体对象实例(VIPState,MemberSate,VisitorState)决定了ClubAccount类对象的不同具体表现
//因为ClubAccount类的Deposit,Cost,Service动作都是依托(VIPState,MemberSate,VisitorState)类对象来实现的
private ClubState _state;
public ClubState State
{
get { return _state; }
set { _state = value; }
}
#endregion
#region 帐户Balance属性
public double Balance
{
get { return _state.Balance; }
}
#endregion
#region 构造函数
public ClubAccount(string name)
{
// 新注册的客户默认为Visitor类型客户状态
//同时通过此处的this把Account帐户与State关联起来
this.CustomerName = name;
this.State = new VisitorState(0.0, this);
}
public ClubAccount(string name,ClubState state)
{
this.CustomerName = name;
this.State = state;
}
#endregion
#region 存款
public void Deposit(double amount)
{
_state.Deposit(amount);
}
#endregion
#region 取款
public void Cost(double amount)
{
_state.Cost(amount);
}
#endregion
#region 俱乐部相关服务
public void ClubService()
{
_state.ClubService();
}
#endregion
}
}
2、抽象状态角色State:ClubState
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyStatePattern
{
//俱乐部会员状态类
abstract class ClubState
{
protected double lowerLimit; //下限
protected double upperLimit; //上限
#region Account属性
protected ClubAccount account;
public ClubAccount Account
{
get { return account; }
set { account = value; }
}
#endregion
#region Balance属性
protected double balance;
public double Balance
{
get { return balance; }
set { balance = value; }
}
#endregion
public abstract void Deposit(double amount); //存钱
public abstract void Cost(double amount); //消费
public abstract void ClubService(); //享受服务
}
}
3、具体状态角色Concrete State:ClubRealStates
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyStatePattern
{
//这时定义一系列具体的State类
//这些类继承自抽象的ClubState类,并完成ClubState类定义的功能
//其中包括状态判断与切换,它们将根据ClubAccount的当前balance属性来决定是否切换状态以及向哪个状态切换
//而ClubAccount已经在构造ClubState类时作为属性传递给了它
#region 一般消费者
class VisitorState : ClubState
{
//构造函数中必须有ClubState对象,因为它必须像接力棒一样把上一个状态的State对象交接过来,在进行状态切换的同时保留上一级状态的历史数据
public VisitorState(ClubState state ):
this(state.Balance,state.Account)
{
}
public VisitorState(double balance,ClubAccount account)
{
this.balance = balance;
this.Account = account;
Initialize();
}
private void Initialize()
{
lowerLimit = 0.0;
upperLimit = 100;
}
public override void Deposit(double amount)
{
balance += amount;
Console.WriteLine("你这次存入了{0}元", amount.ToString());
StateChangeCheck();
}
public override void Cost(double amount)
{
balance -= amount;
Console.WriteLine("你这次消费了{0}元", amount.ToString());
StateChangeCheck();
}
private void StateChangeCheck()
{
if (balance >= upperLimit)
{
account.State = new MemberState(this);
Console.WriteLine("你当前帐户金额为 {0},你可以享受‘普通会员’服务!",balance.ToString());
}
}
public override void ClubService()
{
Console.WriteLine("你申请了服务==>俱乐部提供一般客户的服务内容!");
}
}
#endregion
#region 普通会员
class MemberState : ClubState
{
//构造函数中必须有ClubState对象,因为它必须像接力棒一样把上一个状态的State对象交接过来,在进行状态切换的同时保留上一级状态的历史数据
public MemberState(ClubState state)
: this(state.Balance, state.Account)
{
this.balance = state.Balance;
}
public MemberState(double balance, ClubAccount service)
{
this.balance = balance;
this.account = service;
Initialize();
}
private void Initialize()
{
lowerLimit = 100.0;
upperLimit = 1000;
}
public override void Deposit(double amount)
{
balance += amount;
Console.WriteLine("你这次存入了{0}元", amount.ToString());
StateChangeCheck();
}
public override void Cost(double amount)
{
balance -= amount;
Console.WriteLine("你这次消费了{0}元", amount.ToString());
StateChangeCheck();
}
private void StateChangeCheck()
{
if (balance < lowerLimit)
{
account.State = new VisitorState(this);
Console.WriteLine("你当前帐户金额为 {0},你只能享受‘访客级会员’服务!", balance.ToString());
}
else if(balance >= upperLimit )
{
account.State = new VIPState(this);
Console.WriteLine("你当前帐户金额为 {0},你可以享受‘贵宾级会员’服务!", balance.ToString());
}
}
public override void ClubService()
{
Console.WriteLine("你申请了服务==>俱乐部提供普通会员的服务内容!");
}
}
#endregion
#region 贵宾会员
class VIPState : ClubState
{
//构造函数中必须有ClubState对象,因为它必须像接力棒一样把上一个状态的State对象交接过来,在进行状态切换的同时保留上一级状态的历史数据
public VIPState(ClubState state):this(state.Balance,state.Account)
{
this.balance = state.Balance;
}
public VIPState(double balance, ClubAccount service)
{
this.balance = balance;
this.account = service;
Initialize();
}
private void Initialize()
{
lowerLimit = 1000.0;
}
public override void Deposit(double amount)
{
balance += amount;
Console.WriteLine("你这次存入了{0}元", amount.ToString());
StateChangeCheck();
}
public override void Cost(double amount)
{
balance -= amount;
Console.WriteLine("你这次消费了{0}元,你的帐户还有{1}元",amount.ToString(),balance.ToString());
StateChangeCheck();
}
private void StateChangeCheck()
{
if(balance < lowerLimit)
{
account.State = new MemberState(this);
Console.WriteLine("你当前帐户金额为 {0},你降级为‘普通级会员’服务!", balance.ToString());
}
}
public override void ClubService()
{
Console.WriteLine("你申请了服务==>俱乐部提供贵宾会员的服务内容!");
}
}
#endregion
}
4、客户应用:
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
#region 俱乐部会员服务的状态模式实现
Console.WriteLine("-------------俱乐部会员服务的状态模式实现--------------");
ClubAccount account = new ClubAccount("刘斌");
account.Deposit(100);
account.Deposit(300);
account.Deposit(1000);
account.ClubService();
account.Cost(100);
account.Cost(1000);
account.Cost(300);
account.ClubService();
Console.ReadKey();
#endregion
运行效果如下: