I have an application that has multiple states, with each state responding to input differently.
The initial implementation was done with a big switch statement, which I refactored using the state pattern (at least, I think it’s the state pattern. I’m kind of new to using design patterns, so I tend to get them confused) –
class App { public: static App * getInstance(); void addState(int state_id, AppState * state) { _states[state_id] = state; } void setCurrentState(int state_id) { _current_state = _states[state_id]; } private: App() ~App(); std::map<int, AppState *> _states; AppState * _current_state; static App * _instance; } class AppState { public: virtual void handleInput() = 0; virtual ~AppState(); protected: AppState(); }
Currently, each state is polling the OS for input, and acting accordingly. This means each concrete state has a huge switch statement with a case for each valid keypress. Some cases call functions, and other cases issue state changes by using App::setCurrentState(newstate). The catch is that a key that does something in one state may not do anything (or in rare circumstances, may do something different) in another state.
Okay, I think that’s the pertinent background. Here’s the actual question(s) –
First, what’s the best way to eliminate the huge switch statements in the concrete states? This question suggests the command pattern, but I don’t understand how I would use it here. Can someone help explain it, or suggest another solution?
As a side note, I’ve considered (and am not opposed to) the idea of letting the App class do the polling of the OS, and then pass inputs to _current_state->handleInput. In fact, something tells me that I’ll want to do this as part of the refactoring. I just haven’t done it yet.
Second, state changes are made by calling App::setCurrentState(newstate). I realize that this is akin to using globals, but I’m not sure of a better way to do it. My main goal is to be able to add states without modifying the App class. Suggestions would be welcome here as well.
Given your refactoring, it looks like the question now is how to reduce the amount of keycode parsing code that will be duplicated across your various concrete AppState implementations. As you mention, this leads to multiple switch statements which select which code to call to handle the keystroke input.
Depending on how performance critical this code is, you can separate that keycode decoding logic into a processInput(int keycode) method in App (or as a concrete method in AppState) and create a set of handle*Pressed() functions in your AppState classes. Depending on how many types of keystrokes you are looking at handling, this might be reasonable, or it might lead to far too many methods to implement.