0

In what situation should we adopt state pattern?

I've been assigned to maintain a project, the project state machine was implemented by switch-case that are 2000+ lines long. It will be hard to expand function, so I would like to refactor it. I'm surveying state design pattern, but I have some confusions.

A simple example:

1. Initial state "WAIT", wait user send download command

2. While user send download command, move to "CONNECT" state, connect to server

3. After connection is created, move to "DOWNLOADING" state, keep receive data from server

4. While the data download complete, move to "DISCONNECT", disconnect link with server

5. After disconnect, move to "WAIT" state, wait user send download command

A simple state machine pic

  • Method 1: Before I survey state pattern, I think a trivial method --- wrapper different state behavior in different function, use a function pointer array to point each state function, and change state by call function.

    typedef enum {
        WAIT,
        CONNECT,
        DOWNLOADING,
        DISCONNECT
    }state;
    void (*statefunction[MAX_STATE])(void) = 
    {
        WAITState,
        CONNECTState,
        DOWNLOADINGState,
        DISCONNECTState
    };
    void WAITState(void)
    {
        //do wait behavior
        //while receive download command
        //statefunction[CONNECT]();
    }
    void CONNECTState(void)
    {
        //do connect behavior
        //while connect complete
        //statefunction[DOWNLOADING]();
    }
    void DOWNLOADINGState(void)
    {
        //do downloading behavior
        //while download complete
        //statefunction[DISCONNECT]();
    }
    void DISCONNECTState(void)
    {
        //do disconnect behavior
        //while disconnect complete
        //statefunction[WAIT]();
    }
    
  • Method 2: The state pattern encapsulates different state and its behavior in different class (object-oriented state machine), uses polymorphism to implement different state behavior, and defines a common interface for all concrete states.

    class State
    {
    public:
         virtual void Handle(Context *pContext) = 0;
    };
    class Context
    {
    public:
        Context(State *pState) : m_pState(pState){}
    
        void Request()
        {
            if (m_pState)
            {             
                m_pState->Handle(this);           
            }
         }   
    private:
        State *m_pState;
    };
    class WAIT : public State
    {
    public:
        virtual void Handle(Context *pContext)
        {
            //do wait behavior
        }
    };
    class CONNECT : public State
    {
    public:
        virtual void Handle(Context *pContext)
        {
            //do connect behavior
        }
    };
    class DOWNLOADING : public State
    {
    public:
        virtual void Handle(Context *pContext)
        {
            //do downloading behavior
        }
    };
    class DISCONNECT : public State
    {
    public:
        virtual void Handle(Context *pContext)
        {
            //do disconnect behavior
        }
    };
    

I'm wondering whether the state pattern batter than function pointer in this case or not... Using function pointer only also can improve readability (compare with switch-case), and more simple. The state pattern will create several class, and more complex than using function pointer only. What's the advantage of using state pattern?

Thanks for your time!

jaco0646
  • 15,303
  • 7
  • 59
  • 83
Charlotte
  • 1
  • 1
  • The "state pattern with multiple classes" is more useful in languages where you can't refer to functions themselves, like Java (< 8), but rather *must* use objects. You could view the function pointer variant as an implementation of the pattern, and the multiple-classes variant as a pattern in itself that works around a language limitation (as most "patterns" do). – molbdnilo Nov 19 '15 at 13:12
  • Related, if not duplicate question: http://stackoverflow.com/questions/4935806/how-to-use-state-pattern-correctly/33676953#33676953 – Fuhrmanator Nov 20 '15 at 21:31
  • *"It will be hard to expand function, so I would like to refactor it"* -- Have you been asked to expand it? Will the expansion be new states, new transitions, or both? Your example only shows one transition (Handle) but many state machines have more than one. The OO (Method 2) version requires all states to implement all transitions (since they're defined in the class `State`). Add a few new transitions or states and you can see that it gets complex very fast. If a state doesn't support a transition, it should throw an exception if that method is called. Is that prettier than your case? – Fuhrmanator Nov 20 '15 at 21:48

1 Answers1

0

What's the advantage of using the state pattern?

First, one needs to notice, that both of the methods you've provided, are in fact examples of the very same pattern. One of the methods describes a function-based implementation, while the other one takes more of an object oriented approach.

That being said, the pattern itself has a few advantages:

  1. It limits the number of states, a program can be in, and thus - eliminates undefined states,
  2. It allows for easier expansion of the application, by adding new states, instead of refactoring the whole code,
  3. From a company perspective, it is safe, even when multiple people work on the same class,

Since you tagged the question as related to , it is best to take into account what the language both gives and requires. While classes offer inheritance, a large number of classes can greatly increase the compilation time. Hence, when it comes to implementations, if your state machine is large, static polymorphism may be the way to go.

Paweł Stawarz
  • 3,952
  • 2
  • 17
  • 26