• 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状态。下面以这样的一个场景实现下状态者模式,具体实现代码如下所示:

      1 namespace StatePatternSample
      2 {
      3     public class Account
      4     {
      5         public State State {get;set;}
      6         public string Owner { get; set; }
      7         public Account(string owner)
      8         {
      9             this.Owner = owner;
     10             this.State = new SilverState(0.0, this);
     11         }
     12 
     13         public double Balance { get {return State.Balance; }} // 余额
     14         // 存钱
     15         public void Deposit(double amount)
     16         {
     17             State.Deposit(amount);
     18             Console.WriteLine("存款金额为 {0:C}——", amount);
     19             Console.WriteLine("账户余额为 =:{0:C}", this.Balance);
     20             Console.WriteLine("账户状态为: {0}", this.State.GetType().Name);
     21             Console.WriteLine();
     22         }
     23 
     24         // 取钱
     25         public void Withdraw(double amount)
     26         {
     27             State.Withdraw(amount);
     28              Console.WriteLine("取款金额为 {0:C}——",amount);
     29             Console.WriteLine("账户余额为 =:{0:C}", this.Balance);
     30             Console.WriteLine("账户状态为: {0}", this.State.GetType().Name);
     31             Console.WriteLine();
     32         }
     33 
     34         // 获得利息
     35         public void PayInterest()
     36         {
     37             State.PayInterest();
     38             Console.WriteLine("Interest Paid --- ");
     39             Console.WriteLine("账户余额为 =:{0:C}", this.Balance);
     40             Console.WriteLine("账户状态为: {0}", this.State.GetType().Name);
     41             Console.WriteLine();
     42         }
     43     }
     44 
     45     // 抽象状态类
     46     public abstract class State
     47     {
     48         // Properties
     49         public Account Account { get; set; }
     50         public double Balance { get; set; } // 余额
     51         public double Interest { get; set; } // 利率
     52         public double LowerLimit { get; set; } // 下限
     53         public double UpperLimit { get; set; } // 上限
     54 
     55         public abstract void Deposit(double amount); // 存款
     56         public abstract void Withdraw(double amount); // 取钱
     57         public abstract void PayInterest(); // 获得的利息
     58     }
     59 
     60     // Red State意味着Account透支了
     61     public class RedState : State
     62     {
     63         public RedState(State state)
     64         {
     65             // Initialize
     66             this.Balance = state.Balance;
     67             this.Account = state.Account;
     68             Interest = 0.00;
     69             LowerLimit = -100.00;
     70             UpperLimit = 0.00;
     71         }
     72 
     73         // 存款
     74         public override void Deposit(double amount)
     75         {
     76             Balance += amount;
     77             StateChangeCheck();
     78         }
     79         // 取钱
     80         public override void Withdraw(double amount)
     81         {
     82             Console.WriteLine("没有钱可以取了!");
     83         }
     84 
     85         public override void PayInterest()
     86         {
     87             // 没有利息
     88         }
     89 
     90         private void StateChangeCheck()
     91         {
     92             if (Balance > UpperLimit)
     93             {
     94                 Account.State = new SilverState(this);
     95             }
     96         }
     97     }
     98 
     99     // Silver State意味着没有利息得
    100     public class SilverState :State
    101     {
    102         public SilverState(State state)
    103             : this(state.Balance, state.Account)
    104         { 
    105         }
    106 
    107         public SilverState(double balance, Account account)
    108         {
    109             this.Balance = balance;
    110             this.Account = account;
    111             Interest = 0.00;
    112             LowerLimit = 0.00;
    113             UpperLimit = 1000.00;
    114         }
    115 
    116         public override void Deposit(double amount)
    117         {
    118             Balance += amount;
    119             StateChangeCheck();
    120         }
    121         public override void Withdraw(double amount)
    122         {
    123             Balance -= amount;
    124             StateChangeCheck();
    125         }
    126 
    127         public override void PayInterest()
    128         {
    129             Balance += Interest * Balance;
    130             StateChangeCheck();
    131         }
    132 
    133         private void StateChangeCheck()
    134         {
    135             if (Balance < LowerLimit)
    136             {
    137                 Account.State = new RedState(this);
    138             }
    139             else if (Balance > UpperLimit)
    140             {
    141                 Account.State = new GoldState(this);
    142             }
    143         }     
    144     }
    145 
    146     // Gold State意味着有利息状态
    147     public class GoldState : State
    148     {
    149         public GoldState(State state)
    150         {
    151             this.Balance = state.Balance;
    152             this.Account = state.Account;
    153             Interest = 0.05;
    154             LowerLimit = 1000.00;
    155             UpperLimit = 1000000.00;
    156         }
    157         // 存钱
    158         public override void Deposit(double amount)
    159         {
    160             Balance += amount;
    161             StateChangeCheck();
    162         }
    163         // 取钱
    164         public override void Withdraw(double amount)
    165         {
    166             Balance -= amount;
    167             StateChangeCheck();
    168         }
    169         public override void PayInterest()
    170         {
    171             Balance += Interest * Balance;
    172             StateChangeCheck();
    173         }
    174 
    175         private void StateChangeCheck()
    176         {
    177             if (Balance < 0.0)
    178             {
    179                 Account.State = new RedState(this);
    180             }
    181             else if (Balance < LowerLimit)
    182             {
    183                 Account.State = new SilverState(this);
    184             }
    185         }
    186     }
    187 
    188     class App
    189     {
    190         static void Main(string[] args)
    191         {
    192             // 开一个新的账户
    193             Account account = new Account("Learning Hard");
    194 
    195             // 进行交易
    196             // 存钱
    197             account.Deposit(1000.0);
    198             account.Deposit(200.0);
    199             account.Deposit(600.0);
    200 
    201             // 付利息
    202             account.PayInterest();
    203 
    204             // 取钱
    205             account.Withdraw(2000.00);
    206             account.Withdraw(500.00);
    207             
    208             // 等待用户输入
    209             Console.ReadKey();
    210         }
    211     }
    212 }

      上面代码的运行结果如下图所示:

      从上图可以发现,进行存取款交易,会影响到Account内部的状态,由于状态的改变,从而影响到Account类行为的改变,而且这些操作都是发生在运行时的。

    三、应用状态者模式完善中介者模式方案

      在上一篇博文中,我曾介绍到中介者模式存在的问题,详细的问题描述可以参考上一篇博文。下面利用观察者模式和状态者模式来完善中介者模式,具体的实现代码如下所示:

      1  // 抽象牌友类
      2     public abstract class AbstractCardPartner
      3     {
      4         public int MoneyCount { get; set; }
      5 
      6         public AbstractCardPartner()
      7         {
      8             MoneyCount = 0;
      9         }
     10 
     11         public abstract void ChangeCount(int Count, AbstractMediator mediator);
     12     }
     13 
     14     // 牌友A类
     15     public class ParterA : AbstractCardPartner
     16     {
     17         // 依赖与抽象中介者对象
     18         public override void ChangeCount(int Count, AbstractMediator mediator)
     19         {
     20             mediator.ChangeCount(Count);
     21         }
     22     }
     23 
     24     // 牌友B类
     25     public class ParterB : AbstractCardPartner
     26     {
     27         // 依赖与抽象中介者对象
     28         public override void ChangeCount(int Count, AbstractMediator mediator)
     29         {
     30             mediator.ChangeCount(Count);
     31         }
     32     }
     33 
     34     // 抽象状态类
     35     public abstract class State
     36     {
     37         protected AbstractMediator meditor;
     38         public abstract void ChangeCount(int count);
     39     }
     40 
     41     // A赢状态类
     42     public class AWinState : State
     43     {
     44         public AWinState(AbstractMediator concretemediator)
     45         {
     46             this.meditor = concretemediator;
     47         }
     48 
     49         public override void ChangeCount(int count)
     50         {
     51             foreach (AbstractCardPartner p in meditor.list)
     52             {
     53                 ParterA a = p as ParterA;
     54                 // 
     55                 if (a != null)
     56                 {
     57                     a.MoneyCount += count;
     58                 }
     59                 else
     60                 {
     61                     p.MoneyCount -= count;
     62                 }
     63             }
     64         }
     65     }
     66 
     67     // B赢状态类
     68     public class BWinState : State
     69     {
     70         public BWinState(AbstractMediator concretemediator)
     71         {
     72             this.meditor = concretemediator;
     73         }
     74 
     75         public override void ChangeCount(int count)
     76         {
     77             foreach (AbstractCardPartner p in meditor.list)
     78             {
     79                 ParterB b = p as ParterB;
     80                 // 如果集合对象中时B对象,则对B的钱添加
     81                 if (b != null)
     82                 {
     83                     b.MoneyCount += count;
     84                 }
     85                 else
     86                 {
     87                     p.MoneyCount -= count;
     88                 }
     89             }
     90         }
     91     }
     92 
     93     // 初始化状态类
     94     public class InitState : State
     95     {
     96         public InitState()
     97         {
     98             Console.WriteLine("游戏才刚刚开始,暂时还有玩家胜出");
     99         }
    100 
    101         public override void ChangeCount(int count)
    102         {
    103             // 
    104             return;
    105         }
    106     }
    107 
    108     // 抽象中介者类
    109     public abstract class AbstractMediator
    110     {
    111         public List<AbstractCardPartner> list = new List<AbstractCardPartner>();
    112 
    113         public State State { get; set; }
    114 
    115         public AbstractMediator(State state)
    116         {
    117             this.State = state;
    118         }
    119 
    120         public void Enter(AbstractCardPartner partner)
    121         {
    122             list.Add(partner);
    123         }
    124 
    125         public void Exit(AbstractCardPartner partner)
    126         {
    127             list.Remove(partner);
    128         }
    129 
    130         public void ChangeCount(int count)
    131         {
    132             State.ChangeCount(count);
    133         }
    134     }
    135 
    136     // 具体中介者类
    137     public class MediatorPater : AbstractMediator
    138     {
    139         public MediatorPater(State initState)
    140             : base(initState)
    141         { }
    142     }
    143 
    144     class Program
    145     {
    146         static void Main(string[] args)
    147         {
    148             AbstractCardPartner A = new ParterA();
    149             AbstractCardPartner B = new ParterB();
    150             // 初始钱
    151             A.MoneyCount = 20;
    152             B.MoneyCount = 20;
    153 
    154             AbstractMediator mediator = new MediatorPater(new InitState());
    155 
    156             // A,B玩家进入平台进行游戏
    157             mediator.Enter(A);
    158             mediator.Enter(B);
    159 
    160             // A赢了
    161             mediator.State = new AWinState(mediator);
    162             mediator.ChangeCount(5);
    163             Console.WriteLine("A 现在的钱是:{0}", A.MoneyCount);// 应该是25
    164             Console.WriteLine("B 现在的钱是:{0}", B.MoneyCount); // 应该是15
    165 
    166             // B 赢了
    167             mediator.State = new BWinState(mediator);
    168             mediator.ChangeCount(10);
    169             Console.WriteLine("A 现在的钱是:{0}", A.MoneyCount);// 应该是25
    170             Console.WriteLine("B 现在的钱是:{0}", B.MoneyCount); // 应该是15
    171             Console.Read();
    172         }
    173     }
    View Code

    四、状态者模式的应用场景

       在以下情况下可以考虑使用状态者模式。

    • 当一个对象状态转换的条件表达式过于复杂时可以使用状态者模式。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简单化。
    • 当一个对象行为取决于它的状态,并且它需要在运行时刻根据状态改变它的行为时,就可以考虑使用状态者模式。

    五、状态者模式的优缺点

       状态者模式的主要优点是:

    • 将状态判断逻辑每个状态类里面,可以简化判断的逻辑。
    • 当有新的状态出现时,可以通过添加新的状态类来进行扩展,扩展性好。

      状态者模式的主要缺点是:

    • 如果状态过多的话,会导致有非常多的状态类,加大了开销。

    六、总结

      状态者模式是对对象状态的抽象,从而把对象中对状态复杂的判断逻辑已到各个状态类里面,从而简化逻辑判断。在下一篇文章将分享我对策略模式的理解。

  • 相关阅读:
    岁月静好
    一代才女林徽因 人生若只如初见
    HTML5手机网站开发页面宽度解决方案
    上帝是公平的
    我一直在坚持,你呢?
    让生命更美丽!
    网页跳转,可以任意规定延迟显示的时间
    !important的用法
    小站初建
    GTD之图表
  • 原文地址:https://www.cnblogs.com/zhili/p/StatePattern.html
Copyright © 2020-2023  润新知