The state pattern

State mode is used to solve the state transition of complex objects in the system and the problem of encapsulation under different states. Define an object that manages the state and allows the program to make changes accordingly.

When there is a lot of if… else… Or the switch… Case These branch statements, as in:

if(a){
    // Change the original code every time you add a state
}else if(b){
    //      
}else if(c){
    //      
}else if(d){
    //      
}else{
    //
}
Copy the code

You can use state mode when you have a large number of states that are not easy to manage, and you need to modify the original code every time you add a state (not conforming to the open and close principle).

The state mode consists primarily of two roles

Status class: Manages the status

Environment class: Changes its state

The sample a

For example, a person will do different things when he meets different circumstances in his life, what he will do when he is happy, what he will do when he is sad, what he will do when he is angry…

// The state class is abstract
class State{
    toDo(){
        console.log('Do different things in different states'); }}// Use the state mode, each subsequent state can be added directly below, without modifying the above source code
// Happy state
class Happy extends State{
   toDo(){
     console.log("Eat when you're happy."); }}// Sad state
class UnHappy extends State{
   toDo(){
     console.log("Cry when you're sad."); }}// Be angry
class Angry extends State{
   toDo(){
     console.log("Make a scene when you're angry."); }}// Environment Class Different living environments can cause different states
class Life{
   changeState(state){ // Receive an instance of the state
     state.toDo(); // Perform the corresponding behavior in the corresponding state instance}}// Instantiate the living environment
let life = new Life();

// Pass in happiness
life.changeState(new Happy()); // Have a big meal when you are happy

// incoming sadness
life.changeState(new UnHappy()); // Cry when you are sad

// Incoming anger
life.changeState(new Angry()); // Make a scene when angry
Copy the code

Example 2

Now use code to describe this scene, first define a Light class, it can be foreseen that the Light object Light will be created from the Light class, Light object will have two properties, we use state to record the current state of the Light, button represents the specific switch button.

var Light = function(){
    this.state = 'off'; // Set the lamp to initial state off
    this.button = null; // Light switch button
};

Light.prototype.init = function(){
    var button = document.createElement( 'button' ),
        self = this;
    button.innerHTML = 'switch';
    this.button = document.body.appendChild( button );
    this.button.onclick = function(){ self.buttonWasPressed(); }}; Light.prototype.buttonWasPressed =function(){
    if ( this.state === 'off') {console.log( 'turn on the light' );
        this.state = 'on';
    }else if ( this.state === 'on') {console.log( 'off' );
        this.state = 'off'; }};var light = new Light();

light.init();

Copy the code

The logic of the above code is very clear, but there may be many kinds of light bulbs, some light bulbs are not only on and off two states, there are first press, second press, third press and so on, each time the effect is different. If we just pile up if else, we can’t achieve the effect we want

For more complex cases, we have to change our code again

When we talk about encapsulation, we tend to encapsulate the behavior of an object first, not its state. In state mode, however, the opposite is true. The key to state mode is to encapsulate each state of a thing into a separate class, and the behavior related to this state is encapsulated within this class

We then implement a new business requirement: the first press to turn on low light, the second press to turn on strong light, and the third press to turn off lights

/ / OffLightState:
var OffLightState = function( light ){
    this.light = light;
};

OffLightState.prototype.buttonWasPressed = function(){
    console.log( 'weak light' ); // offLightState Corresponds to the behavior
    this.light.setState( this.light.weakLightState ); // Switch the state to weakLightState
};

/ / WeakLightState:
var WeakLightState = function( light ){
    this.light = light;
};

WeakLightState.prototype.buttonWasPressed = function(){
    console.log( 'light' ); // The corresponding behavior of weakLightState
    this.light.setState( this.light.strongLightState ); // Switch the state to strongLightState
};

/ / StrongLightState:
var StrongLightState = function( light ){
    this.light = light;
};

StrongLightState.prototype.buttonWasPressed = function(){
    console.log( 'off' ); // strongLightState corresponds to the behavior
    this.light.setState( this.light.offLightState ); // Switch the state to offLightState
};

var Light = function(){
    this.offLightState = new OffLightState( this );
    this.weakLightState = new WeakLightState( this );
    this.strongLightState = new StrongLightState( this );
    this.button = null;
};

Light.prototype.init = function(){
    var button = document.createElement( 'button' ),
        self = this; 
    this.button = document.body.appendChild( button );
    this.button.innerHTML = 'switch';
    this.currState = this.offLightState; // Set the current state
    this.button.onclick = function(){ self.currState.buttonWasPressed(); }}; Light.prototype.setState =function( newState ){
    this.currState = newState;
};

var light = new Light();

light.init();
Copy the code

The benefits of using a state pattern are obvious: it localizes the relationship between each state and its corresponding behavior, which is dispersed and encapsulated in its corresponding state class for easy reading and management of code.

In addition, switching between states is distributed within the state class, which eliminates the need to write a lot of if and else conditional branching languages to control switching between states.

When we need to add a new state to the Light object, we simply add a new state class and change the existing code slightly.