State – This pattern can let the program’s object alter its behavior when its internal state changes, which seems like it changes its class.

From Design Pattern – Behavioral Patterns

Structure

State pattern – Class diagram UML

Applicability

  • Using this pattern, when the program has an object that behaves differently depending on its current state, the number of states is enormous, and the state-specific code changes frequently.
  • Use this pattern when the program has a class polluted with massive conditionals that alter how the class behaves according to the current values of the class’s fields.
  • Use this pattern when the program has a lot of duplicate code across similar states and transitions of a condition-based state machine.

Note: This pattern seems like a good solution for any situation, but it brings a few problems—for example, Finite-State Machine.

Pros and Cons

PROS

  • Single Responsibility Principle – Organize the code related to a particular state into separate classes.
  • Open-Closed Principle – Introduce new states without changing existing state classes or the context.
  • Simplify the context code by eliminating bulky state machine conditionals.

CONS

Applying this pattern can be overkill if a state machine has only a few states or rarely changes.

How to Implement

#1 Basic Sample

static void Main(string[] args)
{
	var context = new Context(new ConcreteStateA());
    	context.Request1();
    	context.Request2();

    	Console.ReadKey();
}

class Context
{
    	private State _state = null;

    	public Context(State state)
    	{
		TransitionTo(state);
    	}

    	public void TransitionTo(State state)
    	{
		Console.WriteLine($"Context: Transition to {state.GetType().Name}.");
		_state = state;
		_state.SetContext(this);
    	}

    	public void Request1() => _state.Handle1();

    	public void Request2() => _state.Handle2();
}

abstract class State
{
    	protected Context _context;

    	public void SetContext(Context context) => _context = context;

    	public abstract void Handle1();

    	public abstract void Handle2();
}

class ConcreteStateA : State
{
    	public override void Handle1()
    	{
		Console.WriteLine("ConcreteStateA handles request1.");
		Console.WriteLine("ConcreteStateA wants to change the state of the context.");
		_context.TransitionTo(new ConcreteStateB());
    	}

    	public override void Handle2() => Console.WriteLine("ConcreteStateA handles request2.");    	
}

class ConcreteStateB : State
{
    	public override void Handle1() => Console.Write("ConcreteStateB handles request1.");
    	
    	public override void Handle2()
    	{
		Console.WriteLine("ConcreteStateB handles request2.");
		Console.WriteLine("ConcreteStateB wants to change the state of the context.");
		_context.TransitionTo(new ConcreteStateA());
    	}
}

#2 Web Application Project

Step 1 Create a state context of the interface class file, which name sets “IState_Context.” Create a second interface class file, which name sets “IStateServices.”

Fig 1 IState_Context interface class file source code
Fig 2 IStateService interface class file source code

Step 2 Create the state class object files and a state context class file in the Core projects. The class file name of the state context sets “State_Context.” The state class file name sets “State.” The “State_Context” inherits an “IState_Context” interface class file.

Fig 3 State_Context class file source code
Fig 4 State class file source code

Step 3 Create the two class files about the state node of the state pattern. One file name of the state node sets “ConcreteState.” Other class file name sets “AnotherConcreteState.”

Fig 5 ConcreteState class file source code
Fig 6 AnotherConcreteState class file source code

Step 4 Create the service class file of the state pattern, which inherits an “IStateServices” interface class file.

Fig 7 StateServices class file source code

Step 5 Create the state pattern-related file in the Web projects, then register the interface class object of the state pattern and the service class object of the state pattern.

// State
services.AddScoped(typeof(IStateServices<>), typeof(StateServices<>));
Fig 8 State pattern result.

Reference

1 Comment

Leave a Reply