首页 > 代码库 > C#设计模式-状态者模式

C#设计模式-状态者模式

一、 状态者(State)模式

每个对象都有其对应的状态,而每个状态又对应一些相应的行为,如果某个对象有多个状态时,那么就会对应很多的行为。那么对这些状态的判断和根据状态完成的行为,就会导致多重条件语句,并且如果添加一种新的状态时,需要更改之前现有的代码。这样的设计显然违背了开闭原则。状态模式正是用来解决这样的问题的。状态模式将每种状态对应的行为抽象出来成为单独新的对象,这样状态的变化不再依赖于对象内部的行为。

状态模式——允许一个对象在其内部状态改变时自动改变其行为,对象看起来就像是改变了它的类。

二、 状态者模式的结构

既然状态者模式是对已有对象的状态进行抽象,则自然就有抽象状态者类和具体状态者类,而原来已有对象需要保存抽象状态者类的引用,通过调用抽象状态者的行为来改变已有对象的行为。经过上面的分析,状态者模式的结构图也就很容易理解了,具体结构图如下图示。

技术分享

从上图可知,状态者模式涉及以下三个角色:

  • Account类:维护一个State类的一个实例,该实例标识着当前对象的状态。
  • State类:抽象状态类,定义了一个具体状态类需要实现的行为约定。
  • SilveStater、GoldState和RedState类:具体状态类,实现抽象状态类的每个行为。

三、 状态者模式的实现

下面,就以银行账户的状态来实现下状态者模式。银行账户根据余额可分为RedState、SilverState和GoldState。这些状态分别代表透支账号,新开账户和标准账户。账号余额在【-100.0,0.0】范围表示处于RedState状态,账号余额在【0.0 , 1000.0】范围表示处于SilverState,账号在【1000.0, 100000.0】范围表示处于GoldState状态。下面以这样的一个场景实现下状态者模式,具体实现代码如下所示:

using System;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)        {            // 开一个新的账户            var 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();        }    }}

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

账户余额为 =:¥1,000.00账户状态为:SilverState存款金额为 ¥200.00——账户余额为 =:¥1,200.00账户状态为:GoldState存款金额为 ¥600.00——账户余额为 =:¥1,800.00账户状态为:GoldStateInterest Paid ---账户余额为 =:¥1,890.00账户状态为:GoldState取款金额为 ¥2000.00——账户余额为 =:¥-110.00账户状态为:RedState没有钱可以取了!取款金额为 ¥500.00——账户余额为 =:¥-110.00账户状态为:RedState

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

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

我曾介绍到中介者模式存在的问题,下面利用观察者模式和状态者模式来完善中介者模式,具体的实现代码如下所示:

// 抽象牌友类using System;using System.Collections.Generic;public abstract class AbstractCardPartner{    public int MoneyCount { get; set; }    protected AbstractCardPartner()    {        MoneyCount = 0;    }    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)        {            var 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)        {            var 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; }    protected 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 = 20;        b.MoneyCount = 20;        AbstractMediator mediator = new MediatorPater(new InitState());        // A,B玩家进入平台进行游戏        mediator.Enter(a);        mediator.Enter(b);        // A赢了        mediator.State = new AWinState(mediator);        mediator.ChangeCount(5);        Console.WriteLine("A 现在的钱是:{0}", a.MoneyCount);// 应该是25        Console.WriteLine("B 现在的钱是:{0}", b.MoneyCount); // 应该是15        // B 赢了        mediator.State = new BWinState(mediator);        mediator.ChangeCount(10);        Console.WriteLine("A 现在的钱是:{0}", a.MoneyCount);// 应该是25        Console.WriteLine("B 现在的钱是:{0}", b.MoneyCount); // 应该是15        Console.Read();    }}

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

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

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

六、 状态者模式的优缺点

优点:

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

缺点:

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

七、 总结

状态者模式是对对象状态的抽象,从而把对象中对状态复杂的判断逻辑已到各个状态类里面,从而简化逻辑判断。

C#设计模式-状态者模式