Learn State Design Pattern by Designing a Simple Music System

Many times we need to develop systems / objects that provides services to outside world. But the response of each service is not always same, it depends upon its current internal state.

When its internal state changes at run-time then its behaviour changes and same services acts differently.

For example,

Suppose our system is a Music System and it provides a service i.e.

  • Press play button to play the music.
  • Press stop button to stop the music.
  • Press next button to play the next song.
  • Press prev button to play the previous song.

But does the music system always behaves as suggested above ?

NO.

Lets suppose initially our music system is not playing any song hence in STOPPED state.

[showads ad=inside_post]

If music system is in STOPPED states then,

  • But pressing any other button except PLAY like STOP, NEXT and PREV will have no effect.
  • Pressing the PLAY button will play the music and will move the system in PLAYING state.

If music system is in PLAYING states then,

  • Pressing stop button will stop the music and move the music system to STOPPED state.
  • Pressing next button will play the next song.
  • Pressing prev button will play the previous song.

So, 2 states of a Music Systems are,

State Diagram of Music System
State Diagram of Music System

 

How to design for such problems in Object Oriented manner?

Answer is : Using State Design Pattern.

Intent of State Design Pattern:

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

Structure for State Design Pattern:

Context and State Transition
Context and State Transition

Define a “context” class to present a single interface to the outside world.

MusicSystem will be our “context” class that will provide following services,

  • Play Music
  • Stop Music
  • Play Next Song
  • Play Previous Song

Define a State abstract base class and based on different “states” of the state machine create derived classes of the State base class.

As there are two states of music system, So, there will be a state class with following derived classes,

  • StoppedState
  • PlayingState

Define state-specific behaviour in the appropriate State derived classes.

e.g.

In STOPPED State,

  • STOP, NEXT and PREV request will have no effect
  • PLAY Request will play the music and will also change the current State of Music System to PLAYING.

In PLAYING State,

  • PLAY request will have no effect
  • STOP Request will stop the music and will also change the current State of Music System to STOPPED.
  • PREV Request will play the previous song.
  • NEXT Request will play the previous song.

Maintain a pointer to the current “state” in the “context” class.

As, music system will always be one of these 2 states at a time. So, our Music System will contain a pointer to store a state and will use this state while deciding actions.

To change the state of the state machine, change the current “state” pointer.

During any action it might be possible that state of Music System will change and then response of requests after that will depend on new State.

If Stop Music Request came and Music System was in currently PLAYING state then its state will change to STOPPED. After that PREV and NEXT request will have no response until Music System comes again in PLAYING State.

Generic Class Diagram of State Design Pattern,

State Design Pattern Class Diagram
State Design Pattern Class Diagram

Wait a minute ! Context contains the Base State’s pointer that points to the object of one of the derived state class. Then how derived class will change the state of Context ?

Answer is

Reference or Pointer to the context will be passed to state class during the request, then derived state class can change the current state of Context class based on its conditions.

No Lets Design for Music System using State Design Pattern,

Music System Class Diagram
Music System Class Diagram

Lets see each class in detail now,

Here MusicSystem is our Context,


class MusicSystem {
    State * m_CurrentState;
public:
    MusicSystem();
    bool playMusic();
    bool stopMusic();
    bool previousSong();
    bool nextSong();
    void setCurrentState(State * currentState);
};
MusicSystem::MusicSystem() {
    m_CurrentState = new StoppedState();
}
bool MusicSystem::playMusic() {
    if (m_CurrentState)
        return m_CurrentState->playMusic(this);
    return false;
}
bool MusicSystem::stopMusic() {
    if (m_CurrentState)
        return m_CurrentState->stopMusic(this);
    return false;
}
bool MusicSystem::previousSong() {
    if (m_CurrentState)
        return m_CurrentState->previousSong(this);
    return false;
}
bool MusicSystem::nextSong() {
    if (m_CurrentState)
        return m_CurrentState->nextSong(this);
    return false;
}

void MusicSystem::setCurrentState(State * currentState) {
    if (m_CurrentState) {
        delete m_CurrentState;
        m_CurrentState = NULL;
    }
    m_CurrentState = currentState;
}

 

Now Lets Design Base State Class,

class State {
public:
    virtual bool playMusic(MusicSystem * pMusicSys);
    virtual bool stopMusic(MusicSystem * pMusicSys);
    virtual bool previousSong(MusicSystem * pMusicSys);
    virtual bool nextSong(MusicSystem * pMusicSys);

};
bool State::playMusic(MusicSystem * pMusicSys) {
    std::cout << "Sorry Music Cannot be played\n";
    return false;
}
bool State::stopMusic(MusicSystem * pMusicSys) {
    std::cout << "Sorry Music Cannot be played as Music is already stopped\n";
    return false;
}
bool State::previousSong(MusicSystem * pMusicSys) {
    std::cout << "Sorry previous song Cannot be played\n";
    return false;
}
bool State::nextSong(MusicSystem * pMusicSys) {
    std::cout << "Sorry previous song Cannot be played\n";
    return false;
}

First Derived State Class – StoppedState,

class StoppedState: public State {
public:
    bool playMusic(MusicSystem * pMusicSys);

};
bool StoppedState::playMusic(MusicSystem * pMusicSys) {
    pMusicSys->setCurrentState(new PlayingState());
    std::cout << "Started Playing the music\n";
    return true;
}

Second derived State Class – PlayingState,

class PlayingState: public State {
public:
    bool stopMusic(MusicSystem * pMusicSys) {
        pMusicSys->setCurrentState(new StoppedState());
        std::cout << "Stopped Playing the music\n";
        return true;
    }
    bool previousSong(MusicSystem * pMusicSys) {
        std::cout << "Playing the previous song\n";
        return true;
    }
    bool nextSong(MusicSystem * pMusicSys) {
        std::cout << "Playing the next song\n";
        return true;
    }
};

How to use these Music System classes,

int main() {
    MusicSystem obj;
    obj.playMusic();
    obj.nextSong();
    obj.stopMusic();
    obj.previousSong();
    obj.playMusic();
    return 0;
}

This is how we can use State Design Pattern. But wait a minute there are some more points,

  • If we see carefully the above State Design Pattern implementation, then we will found that there are many State objects created and destroyed. Is there any way to minimize such wastage ? Answer is yes using Flyweight Design Pattern.
  • State Design Pattern is different from Strategy Design Pattern in Intent only.

We will discuss above two topics in next article.

Thanks & Happy Designing 🙂

2 thoughts on “Learn State Design Pattern by Designing a Simple Music System”

  1. Thank you for your wonderful explanation.
    However I want to see how would you save the creation of new objects, as you said…

    >If we see carefully the above State Design Pattern implementation, then we will found that >there are many State objects created and destroyed. Is there any way to minimize such >wastage ? Answer is yes using Flyweight Design Pattern.

    Please show this implementation as well

  2. Your explanation about these hard topics seems like we are learning ABCD…You made Design patterns looks so simple when everybody tried to make them a hell to learn…

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top