`
阿尔萨斯
  • 浏览: 4200864 次
社区版块
存档分类
最新评论

Design Pattern - State (C#)

 
阅读更多

Definition

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

UML Class Diagram

Participants

The classes and/or objects participating in this pattern are:

  • Context(Account)
    • Defines the interface of interest to clients
    • Maintains an instance of a ConcreteState subclass that defines the current state.
  • State(State)
    • Defines an interface for encapsulating the behavior associated with a particular state of the Context.
  • Concrete State(RedState, SilverState, GoldState)
    • Each subclass implements a behavior associated with a state of Context

Sample Code in C#

This structural code demonstrates the State pattern which allows an object to behave differently depending on its internal state. The difference in behavior is delegated to objects that represent this state.

// --------------------------------------------------------------------------------------------------------------------
// <copyright company="Chimomo's Company" file="Program.cs">
// Respect the work.
// </copyright>
// <summary>
// Structural State Design Pattern.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace CSharpLearning
{
    using System;

    /// <summary>
    /// Startup class for Structural State Design Pattern.
    /// </summary>
    internal static class Program
    {
        #region Methods

        /// <summary>
        /// Entry point into console application.
        /// </summary>
        private static void Main()
        {
            // Setup context in a state
            var c = new Context(new ConcreteStateA());

            // Issue requests, which toggles state
            c.Request();
            c.Request();
            c.Request();
            c.Request();
        }

        #endregion
    }

    /// <summary>
    /// The 'State' abstract class
    /// </summary>
    internal abstract class State
    {
        #region Public Methods and Operators

        /// <summary>
        /// The handle.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        public abstract void Handle(Context context);

        #endregion
    }

    /// <summary>
    /// A 'ConcreteState' class
    /// </summary>
    internal class ConcreteStateA : State
    {
        #region Public Methods and Operators

        /// <summary>
        /// The handle.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        public override void Handle(Context context)
        {
            context.State = new ConcreteStateB();
        }

        #endregion
    }

    /// <summary>
    /// A 'ConcreteState' class
    /// </summary>
    internal class ConcreteStateB : State
    {
        #region Public Methods and Operators

        /// <summary>
        /// The handle.
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        public override void Handle(Context context)
        {
            context.State = new ConcreteStateA();
        }

        #endregion
    }

    /// <summary>
    /// The 'Context' class
    /// </summary>
    internal class Context
    {
        #region Fields

        /// <summary>
        /// The state.
        /// </summary>
        private State state;

        #endregion

        // Constructor
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Context"/> class.
        /// </summary>
        /// <param name="state">
        /// The state.
        /// </param>
        public Context(State state)
        {
            this.State = state;
        }

        #endregion

        // Gets or sets the state
        #region Public Properties

        /// <summary>
        /// Gets or sets the state.
        /// </summary>
        public State State
        {
            get
            {
                return this.state;
            }

            set
            {
                this.state = value;
                Console.WriteLine("State: " + this.state.GetType().Name);
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The request.
        /// </summary>
        public void Request()
        {
            this.state.Handle(this);
        }

        #endregion
    }
}

// Output:
/*
State: ConcreteStateA
State: ConcreteStateB
State: ConcreteStateA
State: ConcreteStateB
State: ConcreteStateA
*/


This real-world code demonstrates the State pattern which allows an Account to behave differently depending on its balance. The difference in behavior is delegated to State objects called RedState, SilverState and GoldState. These states represent overdrawn accounts, starter accounts, and accounts in good standing.

// --------------------------------------------------------------------------------------------------------------------
// <copyright company="Chimomo's Company" file="Program.cs">
// Respect the work.
// </copyright>
// <summary>
// Real-World State Design Pattern.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace CSharpLearning
{
    using System;

    /// <summary>
    /// Startup class for Real-World State Design Pattern.
    /// </summary>
    internal static class Program
    {
        #region Methods

        /// <summary>
        /// Entry point into console application.
        /// </summary>
        private static void Main()
        {
            // Open a new account
            var account = new Account("Jim Johnson");

            // Apply financial transactions
            account.Deposit(500.0);
            account.Deposit(300.0);
            account.Deposit(550.0);
            account.PayInterest();
            account.Withdraw(2000.00);
            account.Withdraw(1100.00);
        }

        #endregion
    }

    /// <summary>
    /// The 'State' abstract class
    /// </summary>
    internal abstract class State
    {
        #region Fields

        /// <summary>
        /// The account.
        /// </summary>
        protected Account account;

        /// <summary>
        /// The balance.
        /// </summary>
        protected double balance;

        /// <summary>
        /// The interest.
        /// </summary>
        protected double interest;

        /// <summary>
        /// The lower limit.
        /// </summary>
        protected double lowerLimit;

        /// <summary>
        /// The upper limit.
        /// </summary>
        protected double upperLimit;

        #endregion

        // Properties
        #region Public Properties

        /// <summary>
        /// Gets or sets the account.
        /// </summary>
        public Account Account
        {
            get
            {
                return this.account;
            }

            set
            {
                this.account = value;
            }
        }

        /// <summary>
        /// Gets or sets the balance.
        /// </summary>
        public double Balance
        {
            get
            {
                return this.balance;
            }

            set
            {
                this.balance = value;
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The deposit.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public abstract void Deposit(double amount);

        /// <summary>
        /// The pay interest.
        /// </summary>
        public abstract void PayInterest();

        /// <summary>
        /// The withdraw.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public abstract void Withdraw(double amount);

        #endregion
    }

    /// <summary>
    /// A 'ConcreteState' class
    /// <remarks>
    /// Red indicates that account is overdrawn
    /// </remarks>
    /// </summary>
    internal class RedState : State
    {
        #region Fields

        /// <summary>
        /// The service fee.
        /// </summary>
        private double serviceFee;

        #endregion

        // Constructor
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="RedState"/> class.
        /// </summary>
        /// <param name="state">
        /// The state.
        /// </param>
        public RedState(State state)
        {
            this.balance = state.Balance;
            this.account = state.Account;
            this.Initialize();
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The deposit.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public override void Deposit(double amount)
        {
            this.balance += amount;
            this.StateChangeCheck();
        }

        /// <summary>
        /// The pay interest.
        /// </summary>
        public override void PayInterest()
        {
            // No interest is paid
        }

        /// <summary>
        /// The withdraw.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public override void Withdraw(double amount)
        {
            amount = amount - this.serviceFee;
            Console.WriteLine("No funds available for withdrawal!");
        }

        #endregion

        #region Methods

        /// <summary>
        /// The initialize.
        /// </summary>
        private void Initialize()
        {
            // Should come from a data source.
            this.interest = 0.0;
            this.lowerLimit = -100.0;
            this.upperLimit = 0.0;
            this.serviceFee = 15.00;
        }

        /// <summary>
        /// The state change check.
        /// </summary>
        private void StateChangeCheck()
        {
            if (this.balance > this.upperLimit)
            {
                this.account.State = new SilverState(this);
            }
        }

        #endregion
    }

    /// <summary>
    /// A 'ConcreteState' class
    /// <remarks>
    /// Silver indicates a non-interest bearing state
    /// </remarks>
    /// </summary>
    internal class SilverState : State
    {
        // Overloaded constructors
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SilverState"/> class.
        /// </summary>
        /// <param name="state">
        /// The state.
        /// </param>
        public SilverState(State state)
            : this(state.Balance, state.Account)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SilverState"/> class.
        /// </summary>
        /// <param name="balance">
        /// The balance.
        /// </param>
        /// <param name="account">
        /// The account.
        /// </param>
        public SilverState(double balance, Account account)
        {
            this.balance = balance;
            this.account = account;
            this.Initialize();
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The deposit.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public override void Deposit(double amount)
        {
            this.balance += amount;
            this.StateChangeCheck();
        }

        /// <summary>
        /// The pay interest.
        /// </summary>
        public override void PayInterest()
        {
            this.balance += this.interest * this.balance;
            this.StateChangeCheck();
        }

        /// <summary>
        /// The withdraw.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public override void Withdraw(double amount)
        {
            this.balance -= amount;
            this.StateChangeCheck();
        }

        #endregion

        #region Methods

        /// <summary>
        /// The initialize.
        /// </summary>
        private void Initialize()
        {
            // Should come from a data source.
            this.interest = 0.0;
            this.lowerLimit = 0.0;
            this.upperLimit = 1000.0;
        }

        /// <summary>
        /// The state change check.
        /// </summary>
        private void StateChangeCheck()
        {
            if (this.balance < this.lowerLimit)
            {
                this.account.State = new RedState(this);
            }
            else if (this.balance > this.upperLimit)
            {
                this.account.State = new GoldState(this);
            }
        }

        #endregion
    }

    /// <summary>
    /// A 'ConcreteState' class
    /// <remarks>
    /// Gold indicates an interest bearing state
    /// </remarks>
    /// </summary>
    internal class GoldState : State
    {
        // Overloaded constructors
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="GoldState"/> class.
        /// </summary>
        /// <param name="state">
        /// The state.
        /// </param>
        public GoldState(State state)
            : this(state.Balance, state.Account)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GoldState"/> class.
        /// </summary>
        /// <param name="balance">
        /// The balance.
        /// </param>
        /// <param name="account">
        /// The account.
        /// </param>
        public GoldState(double balance, Account account)
        {
            this.balance = balance;
            this.account = account;
            this.Initialize();
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The deposit.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public override void Deposit(double amount)
        {
            this.balance += amount;
            this.StateChangeCheck();
        }

        /// <summary>
        /// The pay interest.
        /// </summary>
        public override void PayInterest()
        {
            this.balance += this.interest * this.balance;
            this.StateChangeCheck();
        }

        /// <summary>
        /// The withdraw.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public override void Withdraw(double amount)
        {
            this.balance -= amount;
            this.StateChangeCheck();
        }

        #endregion

        #region Methods

        /// <summary>
        /// The initialize.
        /// </summary>
        private void Initialize()
        {
            // Should come from a database
            this.interest = 0.05;
            this.lowerLimit = 1000.0;
            this.upperLimit = 10000000.0;
        }

        /// <summary>
        /// The state change check.
        /// </summary>
        private void StateChangeCheck()
        {
            if (this.balance < 0.0)
            {
                this.account.State = new RedState(this);
            }
            else if (this.balance < this.lowerLimit)
            {
                this.account.State = new SilverState(this);
            }
        }

        #endregion
    }

    /// <summary>
    /// The 'Context' class
    /// </summary>
    internal class Account
    {
        #region Fields

        /// <summary>
        /// The owner.
        /// </summary>
        private string owner;

        /// <summary>
        /// The state.
        /// </summary>
        private State state;

        #endregion

        // Constructor
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Account"/> class.
        /// </summary>
        /// <param name="owner">
        /// The owner.
        /// </param>
        public Account(string owner)
        {
            // New accounts are 'Silver' by default
            this.owner = owner;
            this.state = new SilverState(0.0, this);
        }

        #endregion

        // Properties
        #region Public Properties

        /// <summary>
        /// Gets the balance.
        /// </summary>
        public double Balance
        {
            get
            {
                return this.state.Balance;
            }
        }

        /// <summary>
        /// Gets or sets the state.
        /// </summary>
        public State State
        {
            get
            {
                return this.state;
            }

            set
            {
                this.state = value;
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The deposit.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public void Deposit(double amount)
        {
            this.state.Deposit(amount);
            Console.WriteLine("Deposited {0:C} --- ", amount);
            Console.WriteLine(" Balance = {0:C}", this.Balance);
            Console.WriteLine(" Status = {0}", this.State.GetType().Name);
            Console.WriteLine(string.Empty);
        }

        /// <summary>
        /// The pay interest.
        /// </summary>
        public void PayInterest()
        {
            this.state.PayInterest();
            Console.WriteLine("Interest Paid --- ");
            Console.WriteLine(" Balance = {0:C}", this.Balance);
            Console.WriteLine(" Status = {0}\n", this.State.GetType().Name);
        }

        /// <summary>
        /// The withdraw.
        /// </summary>
        /// <param name="amount">
        /// The amount.
        /// </param>
        public void Withdraw(double amount)
        {
            this.state.Withdraw(amount);
            Console.WriteLine("Withdrew {0:C} --- ", amount);
            Console.WriteLine(" Balance = {0:C}", this.Balance);
            Console.WriteLine(" Status = {0}\n", this.State.GetType().Name);
        }

        #endregion
    }
}

// Output:
/*
Deposited $500.00 ---
 Balance = $500.00
 Status = SilverState

Deposited $300.00 ---
 Balance = $800.00
 Status = SilverState

Deposited $550.00 ---
 Balance = $1,350.00
 Status = GoldState

Interest Paid ---
 Balance = $1,417.50
 Status = GoldState

Withdrew $2,000.00 ---
 Balance = ($582.50)
 Status = RedState

No funds available for withdrawal!
Withdrew $1,100.00 ---
 Balance = ($582.50)
 Status = RedState

*/
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics