State¶
- State
- UML class diagram
- Participants
- Structural code in C# ..{10}
- Real-world code in C# .{10}
- .NET Optimized code in C# ..{10}
Summary: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
Frequency of use: Medium
UML class diagram¶
Participants¶
The classes and 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
Structural code in C# ..{10}¶
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.
using System;
namespace DoFactory.GangOfFour.State.Structural
{
/// <summary>
/// MainApp startup class for Structural
/// State Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
// Setup context in a state
Context c = new Context(new ConcreteStateA());
// Issue requests, which toggles state
c.Request();
c.Request();
c.Request();
c.Request();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'State' abstract class
/// </summary>
abstract class State
{
public abstract void Handle(Context context);
}
/// <summary>
/// A 'ConcreteState' class
/// </summary>
class ConcreteStateA : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateB();
}
}
/// <summary>
/// A 'ConcreteState' class
/// </summary>
class ConcreteStateB : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateA();
}
}
/// <summary>
/// The 'Context' class
/// </summary>
class Context
{
private State _state;
// Constructor
public Context(State state)
{
this.State = state;
}
// Gets or sets the state
public State State
{
get { return _state; }
set
{
_state = value;
Console.WriteLine("State: " +
_state.GetType().Name);
}
}
public void Request()
{
_state.Handle(this);
}
}
}
Output¶
State: ConcreteStateA
State: ConcreteStateB
State: ConcreteStateA
State: ConcreteStateB
State: ConcreteStateA
Real-world code in C# .{10}¶
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.
using System;
namespace DoFactory.GangOfFour.State.RealWorld
{
/// <summary>
/// MainApp startup class for Real-World
/// State Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
// Open a new account
Account 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);
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'State' abstract class
/// </summary>
abstract class State
{
protected Account account;
protected double balance;
protected double interest;
protected double lowerLimit;
protected double upperLimit;
// Properties
public Account Account
{
get { return account; }
set { account = value; }
}
public double Balance
{
get { return balance; }
set { balance = value; }
}
public abstract void Deposit(double amount);
public abstract void Withdraw(double amount);
public abstract void PayInterest();
}
/// <summary>
/// A 'ConcreteState' class
/// <remarks>
/// Red indicates that account is overdrawn
/// </remarks>
/// </summary>
class RedState : State
{
private double _serviceFee;
// Constructor
public RedState(State state)
{
this.balance = state.Balance;
this.account = state.Account;
Initialize();
}
private void Initialize()
{
// Should come from a datasource
interest = 0.0;
lowerLimit = -100.0;
upperLimit = 0.0;
_serviceFee = 15.00;
}
public override void Deposit(double amount)
{
balance += amount;
StateChangeCheck();
}
public override void Withdraw(double amount)
{
amount = amount - _serviceFee;
Console.WriteLine("No funds available for withdrawal!");
}
public override void PayInterest()
{
// No interest is paid
}
private void StateChangeCheck()
{
if (balance > upperLimit)
{
account.State = new SilverState(this);
}
}
}
/// <summary>
/// A 'ConcreteState' class
/// <remarks>
/// Silver indicates a non-interest bearing state
/// </remarks>
/// </summary>
class SilverState : State
{
// Overloaded constructors
public SilverState(State state) :
this(state.Balance, state.Account)
{
}
public SilverState(double balance, Account account)
{
this.balance = balance;
this.account = account;
Initialize();
}
private void Initialize()
{
// Should come from a datasource
interest = 0.0;
lowerLimit = 0.0;
upperLimit = 1000.0;
}
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);
}
}
}
/// <summary>
/// A 'ConcreteState' class
/// <remarks>
/// Gold indicates an interest bearing state
/// </remarks>
/// </summary>
class GoldState : State
{
// Overloaded constructors
public GoldState(State state)
: this(state.Balance, state.Account)
{
}
public GoldState(double balance, Account account)
{
this.balance = balance;
this.account = account;
Initialize();
}
private void Initialize()
{
// Should come from a database
interest = 0.05;
lowerLimit = 1000.0;
upperLimit = 10000000.0;
}
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);
}
}
}
/// <summary>
/// The 'Context' class
/// </summary>
class Account
{
private State _state;
private string _owner;
// Constructor
public Account(string owner)
{
// New accounts are 'Silver' by default
this._owner = owner;
this._state = new SilverState(0.0, this);
}
// Properties
public double Balance
{
get { return _state.Balance; }
}
public State State
{
get { return _state; }
set { _state = value; }
}
public void Deposit(double amount)
{
_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("");
}
public void Withdraw(double amount)
{
_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);
}
public void PayInterest()
{
_state.PayInterest();
Console.WriteLine("Interest Paid --- ");
Console.WriteLine(" Balance = {0:C}", this.Balance);
Console.WriteLine(" Status = {0}\n",
this.State.GetType().Name);
}
}
}
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
.NET Optimized code in C# ..{10}¶
using System;
namespace DoFactory.GangOfFour.State.NETOptimized
{
/// <summary>
/// MainApp startup class for .NET optimized
/// State Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
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);
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'State' abstract class
/// </summary>
abstract class State
{
protected double interest;
protected double lowerLimit;
protected double upperLimit;
// Gets or sets the account
public Account Account { get; set; }
// Gets or sets the balance
public double Balance { get; set; }
public abstract void Deposit(double amount);
public abstract void Withdraw(double amount);
public abstract void PayInterest();
}
/// <summary>
/// A 'ConcreteState' class
/// <remarks>
/// Red state indicates account is overdrawn
/// </remarks>
/// </summary>
class RedState : State
{
private double _serviceFee;
// Constructor
public RedState(State state)
{
Balance = state.Balance;
Account = state.Account;
Initialize();
}
private void Initialize()
{
// Should come from a datasource
interest = 0.0;
lowerLimit = -100.0;
upperLimit = 0.0;
_serviceFee = 15.00;
}
public override void Deposit(double amount)
{
Balance += amount;
StateChangeCheck();
}
public override void Withdraw(double amount)
{
amount = amount - _serviceFee;
Console.WriteLine("No funds available for withdrawal!");
}
public override void PayInterest()
{
// No interest is paid
}
private void StateChangeCheck()
{
if (Balance > upperLimit)
{
Account.State = new SilverState(this);
}
}
}
/// <summary>
/// A 'ConcreteState' class
/// <remarks>
/// Silver state is non-interest bearing state
/// </remarks>
/// </summary>
class SilverState : State
{
// Overloaded constructors
public SilverState(State state) :
this(state.Balance, state.Account)
{
}
public SilverState(double balance, Account account)
{
Balance = balance;
Account = account;
Initialize();
}
private void Initialize()
{
// Should come from a datasource
interest = 0.0;
lowerLimit = 0.0;
upperLimit = 1000.0;
}
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);
}
}
}
/// <summary>
/// A 'ConcreteState' class
/// <remarks>
/// Gold incidates interest bearing state
/// </remarks>
/// </summary>
class GoldState : State
{
// Overloaded constructors
public GoldState(State state)
: this(state.Balance, state.Account)
{
}
public GoldState(double balance, Account account)
{
Balance = balance;
Account = account;
Initialize();
}
private void Initialize()
{
// Should come from a database
interest = 0.05;
lowerLimit = 1000.0;
upperLimit = 10000000.0;
}
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);
}
}
}
/// <summary>
/// The 'Context' class
/// </summary>
class Account
{
private string _owner;
// Constructor
public Account(string owner)
{
// New accounts are 'Silver' by default
this._owner = owner;
this.State = new SilverState(0.0, this);
}
// Gets the balance
public double Balance
{
get { return State.Balance; }
}
// Gets or sets state
public State State { get; set; }
public void Deposit(double amount)
{
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("");
}
public void Withdraw(double amount)
{
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);
}
public void PayInterest()
{
State.PayInterest();
Console.WriteLine("Interest Paid --- ");
Console.WriteLine(" Balance = {0:C}", this.Balance);
Console.WriteLine(" Status = {0}\n",
this.State.GetType().Name);
}
}
}