C#设计模式(19)——状态者模式(State Pattern)
一、引言
在上一篇文章介绍到可以使用状态者模式和观察者模式来解决中介者模式存在的问题,在本文中将首先通过一个银行账户的例子来解释状态者模式,通过这个例子使大家可以对状态者模式有一个清楚的认识,接着,再使用状态者模式来解决上一篇文章中提出的问题。
二、状态者模式的介绍
每个对象都有其对应的状态,而每个状态又对应一些相应的行为,如果某个对象有多个状态时,那么就会对应很多的行为。那么对这些状态的判断和根据状态完成的行为,就会导致多重条件语句,并且如果添加一种新的状态时,需要更改之前现有的代码。这样的设计显然违背了开闭原则。状态模式正是用来解决这样的问题的。状态模式将每种状态对应的行为抽象出来成为单独新的对象,这样状态的变化不再依赖于对象内部的行为。
2.1 状态者模式的定义
上面对状态模式做了一个简单的介绍,这里给出状态模式的定义。
状态模式——允许一个对象在其内部状态改变时自动改变其行为,对象看起来就像是改变了它的类。
2.2 状态者模式的结构
既然状态者模式是对已有对象的状态进行抽象,则自然就有抽象状态者类和具体状态者类,而原来已有对象需要保存抽象状态者类的引用,通过调用抽象状态者的行为来改变已有对象的行为。经过上面的分析,状态者模式的结构图也就很容易理解了,具体结构图如下图示。
从上图可知,状态者模式涉及以下三个角色:
- Account类:维护一个State类的一个实例,该实例标识着当前对象的状态。
- State类:抽象状态类,定义了一个具体状态类需要实现的行为约定。
- SilveStater、GoldState和RedState类:具体状态类,实现抽象状态类的每个行为。
2.3 状态者模式的实现
下面,就以银行账户的状态来实现下状态者模式。银行账户根据余额可分为RedState、SilverState和GoldState。这些状态分别代表透支账号,新开账户和标准账户。账号余额在【-100.0,0.0】范围表示处于RedState状态,账号余额在【0.0 , 1000.0】范围表示处于SilverState,账号在【1000.0, 100000.0】范围表示处于GoldState状态。下面以这样的一个场景实现下状态者模式,具体实现代码如下所示:
namespace StatePatternSample
{
public class Account
{
public State State {get;set;}
public string Owner { get; set; }
public Account(string owner)
{
this.Owner = owner;
this.State = new SilverState(0.0, this);
} public double Balance { get {return State.Balance; }} // 余额
// 存钱
public void Deposit(double amount)
{
State.Deposit(amount);
Console.WriteLine("存款金额为 {0:C}——", amount);
Console.WriteLine("账户余额为 =:{0:C}", this.Balance);
Console.WriteLine("账户状态为: {0}", this.State.GetType().Name);
Console.WriteLine();
} // 取钱
public void Withdraw(double amount)
{
State.Withdraw(amount);
Console.WriteLine("取款金额为 {0:C}——",amount);
Console.WriteLine("账户余额为 =:{0:C}", this.Balance);
Console.WriteLine("账户状态为: {0}", this.State.GetType().Name);
Console.WriteLine();
} // 获得利息
public void PayInterest()
{
State.PayInterest();
Console.WriteLine("Interest Paid --- ");
Console.WriteLine("账户余额为 =:{0:C}", this.Balance);
Console.WriteLine("账户状态为: {0}", this.State.GetType().Name);
Console.WriteLine();
}
} // 抽象状态类
public abstract class State
{
// Properties
public Account Account { get; set; }
public double Balance { get; set; } // 余额
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(); // 获得的利息
} // Red State意味着Account透支了
public class RedState : State
{
public RedState(State state)
{
// Initialize
this.Balance = state.Balance;
this.Account = state.Account;
Interest = 0.00;
LowerLimit = -100.00;
UpperLimit = 0.00;
} // 存款
public override void Deposit(double amount)
{
Balance += amount;
StateChangeCheck();
}
// 取钱
public override void Withdraw(double amount)
{
Console.WriteLine("没有钱可以取了!");
} public override void PayInterest()
{
// 没有利息
} private void StateChangeCheck()
{
if (Balance > UpperLimit)
{
Account.State = new SilverState(this);
}
}
} // Silver State意味着没有利息得
public class SilverState :State
{
public SilverState(State state)
: this(state.Balance, state.Account)
{
} public SilverState(double balance, Account account)
{
this.Balance = balance;
this.Account = account;
Interest = 0.00;
LowerLimit = 0.00;
UpperLimit = 1000.00;
} 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)
{
Account.State = new RedState(this);
}
else if (Balance > UpperLimit)
{
Account.State = new GoldState(this);
}
}
} // Gold State意味着有利息状态
public class GoldState : State
{
public GoldState(State state)
{
this.Balance = state.Balance;
this.Account = state.Account;
Interest = 0.05;
LowerLimit = 1000.00;
UpperLimit = 1000000.00;
}
// 存钱
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)
{
Account.State = new RedState(this);
}
else if (Balance < LowerLimit)
{
Account.State = new SilverState(this);
}
}
} class App
{
static void Main(string[] args)
{
// 开一个新的账户
Account account = new Account("Learning Hard"); // 进行交易
// 存钱
account.Deposit(1000.0);
account.Deposit(200.0);
account.Deposit(600.0); // 付利息
account.PayInterest(); // 取钱
account.Withdraw(2000.00);
account.Withdraw(500.00); // 等待用户输入
Console.ReadKey();
}
}
}
上面代码的运行结果如下图所示:
从上图可以发现,进行存取款交易,会影响到Account内部的状态,由于状态的改变,从而影响到Account类行为的改变,而且这些操作都是发生在运行时的。
三、应用状态者模式完善中介者模式方案
在上一篇博文中,我曾介绍到中介者模式存在的问题,详细的问题描述可以参考上一篇博文。下面利用观察者模式和状态者模式来完善中介者模式,具体的实现代码如下所示:
// 抽象牌友类
public abstract class AbstractCardPartner
{
public int MoneyCount { get; set; } public AbstractCardPartner()
{
MoneyCount = ;
} public abstract void ChangeCount(int Count, AbstractMediator mediator);
} // 牌友A类
public class ParterA : AbstractCardPartner
{
// 依赖与抽象中介者对象
public override void ChangeCount(int Count, AbstractMediator mediator)
{
mediator.ChangeCount(Count);
}
} // 牌友B类
public class ParterB : AbstractCardPartner
{
// 依赖与抽象中介者对象
public override void ChangeCount(int Count, AbstractMediator mediator)
{
mediator.ChangeCount(Count);
}
} // 抽象状态类
public abstract class State
{
protected AbstractMediator meditor;
public abstract void ChangeCount(int count);
} // A赢状态类
public class AWinState : State
{
public AWinState(AbstractMediator concretemediator)
{
this.meditor = concretemediator;
} public override void ChangeCount(int count)
{
foreach (AbstractCardPartner p in meditor.list)
{
ParterA a = p as ParterA;
//
if (a != null)
{
a.MoneyCount += count;
}
else
{
p.MoneyCount -= count;
}
}
}
} // B赢状态类
public class BWinState : State
{
public BWinState(AbstractMediator concretemediator)
{
this.meditor = concretemediator;
} public override void ChangeCount(int count)
{
foreach (AbstractCardPartner p in meditor.list)
{
ParterB b = p as ParterB;
// 如果集合对象中时B对象,则对B的钱添加
if (b != null)
{
b.MoneyCount += count;
}
else
{
p.MoneyCount -= count;
}
}
}
} // 初始化状态类
public class InitState : State
{
public InitState()
{
Console.WriteLine("游戏才刚刚开始,暂时还有玩家胜出");
} public override void ChangeCount(int count)
{
//
return;
}
} // 抽象中介者类
public abstract class AbstractMediator
{
public List<AbstractCardPartner> list = new List<AbstractCardPartner>(); public State State { get; set; } public AbstractMediator(State state)
{
this.State = state;
} public void Enter(AbstractCardPartner partner)
{
list.Add(partner);
} public void Exit(AbstractCardPartner partner)
{
list.Remove(partner);
} public void ChangeCount(int count)
{
State.ChangeCount(count);
}
} // 具体中介者类
public class MediatorPater : AbstractMediator
{
public MediatorPater(State initState)
: base(initState)
{ }
} class Program
{
static void Main(string[] args)
{
AbstractCardPartner A = new ParterA();
AbstractCardPartner B = new ParterB();
// 初始钱
A.MoneyCount = ;
B.MoneyCount = ; AbstractMediator mediator = new MediatorPater(new InitState()); // A,B玩家进入平台进行游戏
mediator.Enter(A);
mediator.Enter(B); // A赢了
mediator.State = new AWinState(mediator);
mediator.ChangeCount();
Console.WriteLine("A 现在的钱是:{0}", A.MoneyCount);// 应该是25
Console.WriteLine("B 现在的钱是:{0}", B.MoneyCount); // 应该是15 // B 赢了
mediator.State = new BWinState(mediator);
mediator.ChangeCount();
Console.WriteLine("A 现在的钱是:{0}", A.MoneyCount);// 应该是25
Console.WriteLine("B 现在的钱是:{0}", B.MoneyCount); // 应该是15
Console.Read();
}
}
四、状态者模式的应用场景
在以下情况下可以考虑使用状态者模式。
- 当一个对象状态转换的条件表达式过于复杂时可以使用状态者模式。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简单化。
- 当一个对象行为取决于它的状态,并且它需要在运行时刻根据状态改变它的行为时,就可以考虑使用状态者模式。
五、状态者模式的优缺点
状态者模式的主要优点是:
- 将状态判断逻辑每个状态类里面,可以简化判断的逻辑。
- 当有新的状态出现时,可以通过添加新的状态类来进行扩展,扩展性好。
状态者模式的主要缺点是:
- 如果状态过多的话,会导致有非常多的状态类,加大了开销。
六、总结
状态者模式是对对象状态的抽象,从而把对象中对状态复杂的判断逻辑已到各个状态类里面,从而简化逻辑判断。在下一篇文章将分享我对策略模式的理解。
C#设计模式(19)——状态者模式(State Pattern)的更多相关文章
- 19.状态者模式(State Pattern)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- 乐在其中设计模式(C#) - 状态模式(State Pattern)
原文:乐在其中设计模式(C#) - 状态模式(State Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 状态模式(State Pattern) 作者:webabcd 介绍 允 ...
- 二十四种设计模式:状态模式(State Pattern)
状态模式(State Pattern) 介绍允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它所属的类. 示例有一个Message实体类,对它的操作有Insert()和Get()方法, ...
- 【UE4 设计模式】状态模式 State Pattern
概述 描述 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类. 其别名为状态对象(Objects for States),状态模式是一种对象行为型模式. 有限状态机(FSMs) ...
- [设计模式] 20 状态模式 State Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对状态模式是这样说的:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.状态模式的重点在于状态转换,很多时候,对 ...
- 乐在其中设计模式(C#) - 桥接模式(Bridge Pattern)
原文:乐在其中设计模式(C#) - 桥接模式(Bridge Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 桥接模式(Bridge Pattern) 作者:webabcd 介绍 ...
- 【转】设计模式 ( 十七) 状态模式State(对象行为型)
设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...
- 设计模式 ( 十七) 状态模式State(对象行为型)
设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...
- 乐在其中设计模式(C#) - 提供者模式(Provider Pattern)
原文:乐在其中设计模式(C#) - 提供者模式(Provider Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 提供者模式(Provider Pattern) 作者:weba ...
随机推荐
- TortoiseSVN-1.8.11 安装时弹出2503错误导致安装失败解决办法
这个问题主要是由于msi格式文件在win8中默认不是以管理员身份运行造成,可通过命令行解决: 右键单击win8左下角启动图标,选择命令提示符(管理员): 输入:msiexec /package 要安装 ...
- ntc 热敏电阻
来自维基百科 http://zh.wikipedia.org/zh/%E7%83%AD%E6%95%8F%E7%94%B5%E9%98%BB 热敏电阻的电阻值是根据温度由公式计算而来的,知道这一点就 ...
- 关于64位windows2003 未在本地计算机上注册“Microsoft.Jet.OLEDB.4.0” 的问题
我了个去啊! 在自己机器上测试通过的excel导入功能在客户服务器上死活都不好用,查了半天后来发现客户服务器是64位的win2003!! try catch捕捉问题为:未在本地计算机上注册“Micro ...
- source : not found 原因及解决办法
解决方案来自网络: debian中shell脚本无法使用source的原因及解决方法 现象: shell脚本中source aaa.sh时提示 source: not found 原因: ls -l ...
- DOS批量递归删除文件夹
@echo off for /d /r E:\test01\ %%i in (test) do rd /s /q "%%i" pause
- phpmyadmin
下载地址:https://www.phpmyadmin.net/ 详情:http://baike.baidu.com/link?url=OIngLv0mpiYTZl_sCEmryWkHgUYqZeHr ...
- 绕过/*,web.xml直接访问jsp
web.xml中如果配置了/* 全匹配,那么不能用servet去响应页面返回了,因为全都被会/*拦截. <servlet> <servlet-name>validateAuth ...
- C#拼接地图瓦片
为了在AE程序中使用离线的电子地图,思路如下: 利用下载工具下载地图切片,然后利用C#进行切片拼接成一张图片,最后使用ArcMap进行地理配准,然后发布成ArcGIS Server 切片服务供程序使用 ...
- 如何启动另一个Activity
--------siwuxie95 首先为res->layout下my_layout.xml 的Design添加一个Button,进入Text, android:text 修改为:启动另一个Ac ...
- docker学习使用
安装什么的就略过了,之前已经整理过,这里就说说自己使用中的一些东西,也是初用,记录下(现在使用win10 64位,使用Docker for Windows直接安装就好[需要专业版win10安装hype ...