Many students quickly notice that the UML class diagram for the State Design Pattern is essentially the same as the Strategy Design Pattern. Both patterns use classes that provide concrete implementations of the desired behavior abstracted into an interface (or abstract class) . The primary difference between these two design patterns is the location of the responsibility for knowing what the next state should be when a certain action occurs. In a state diagram certain states can only transition to certain other states and when using the State Design Pattern , this knowledge is given to the concrete implementations of the classes representing these states.
Consider the following state diagram which displays the altered states incorporated into my refactored solution to the stopwatch challenge.
I have split the Running state into two states, RunningToHold and RunningToStop. Which state the running stopwatch is in depends on which button the user clicks to transition the stopwatch to its next state. If the user clicks on the ‘HOLD’ button, the stopwatch was in the RunningToHold state. If the user clicks on the ‘START/STOP’ button, the stopwatch was in the RunningToStop state. Likewise, for the same reason, the Hold state is partitioned into HoldToRunning and HoldToStopped states. These states have the responsibility for configuring what the current state is with respect to whatever button could be clicked in addition to controlling the clock. For example, consider the ZERO state.
The ZeroState class, which represents the state with name ZERO in the state diagram shown above, implements the execute method with the following code.
public void execute()
// resetButton already set to nullState
After starting the clock, it prepares the Start/Stop button for what it should do next if the user were to click on Start/Stop. Likewise, it prepares the Hold Button. Since the Reset button will be unavailable and was already configured that way, no changes are made to the Reset button. For further discussion, assume the user will click on the Start/Stop button again after starting the watch. The RunningToStoppedState will be the state responsible for what is to happen next as shown in the following execute method.
public void execute()
After stopping the clock, the next state is the STOPPED state and the only button activated is the Reset button. Thus the Start/Stop and Hold Buttons are set to NullState and deactivated (colored gray) while the Reset Button is set to StoppedState and activated (colored green). Thus each transition to a new state in the stopwatch machine is accompanied by appropriate configuration of the buttons by activating/inactivating them and informing them what the next state of the machine is. There is no central control of the states of this machine via a monolithic conditional logic structure (the hallmark of procedural programming). If a new state was required, appropriate implementation of the execute method in the new state class plus adjustments in classes transitioning into the new class and out of the new class would be the only changes necessary.
In Challenge One, a Strategy Design Pattern was used to handle the two modes of the Bergin Swing Calculator. The refactored stopwatch also uses a Strategy Design Pattern to handle the two modes of the timer thread, suspended or running. This obviated the need for conditional logic to determine current state of the thread because with the use of the strategy, the thread already knew what state it was in and what to do.
The only conditional logic statements in the application are found in the Clock class where numbers are parsed and formatted. This is an example where conditional logic is not being used to determine the state of a class and hence control its behavior, and thus is out of the scope of the intent of the “Programming Without Ifs” exercises.