Design patterns – Part 8: State pattern
State pattern is probably most used in review cycles. The definition says:
State pattern allows an object to appear as it can change its class by altering its behaviour and state.
What are you talking about?
Well, imagine you are in need to build a bug tracking database. Bug reports in basics have four states. First they are unassigned until someone is solving them. When they are taken over, they become assigned, specifying that someone is already working on them. After bug fix has been produced, it is a good practice to send it to testing. Then it is up to QA to confirm that bug fix is complete or whether it is not.
Show me the code!
Example will be based on simple bug report specified above. However, I will skip actual action implementation as it varies based on application needs. Instead, I will use message box to print out the action report.
So, now, first what you need is a state abstract class that will implement all methods needed for transition between states (in our case: unassigned, assigned, send to testing and complete).
Class CState Sub TakeOver() End Sub Sub SendToTesting() End Sub Sub Complete() End Sub End Class
Then, what you need is a context class that will be used when changing state of a bug report.
Class CStateBugReport
   stateUnassigned As CState
   stateAssigned As CState
   stateReadyForTesting As CState
   stateCompleted As CState
   stateCurrent As CState
   Sub New
      Set stateUnassigned = New CStateUnassigned (Me)
      Set stateAssigned = New CStateAssigned (Me)
      Set stateReadyForTesting = New CStateSentToTesting (Me)
      Set stateCompleted = New CStateCompleted (Me)
      Set stateCurrent = stateUnassignedb
   End Sub
   Sub Assign()
      Call stateCurrent.TakeOver()
   End Sub
   Sub SendToTesting()
      Call stateCurrent.SendToTesting()
   End Sub
   Sub Complete()
      Call stateCurrent.Complete()
   End Sub
   Function GetAssignedState() As CState
      Set GetAssignedState = stateAssigned
   End Function
   Function GetReadyForTestingState() As CState
      Set GetReadyForTestingState = stateReadyForTesting
   End Function
   Function GetCompletedState() As CState
      Set GetCompletedState = stateCompleted
   End Function
   Sub SetState( state As CState )
      Set Me.stateCurrent = state
   End Sub
End Class
And now, we need to implement all four states classes.
Class CStateUnassigned As CState
   bugReport As CStateBugReport
   Sub New (bugReport As CStateBugReport)
      Set Me.bugReport = bugReport
   End Sub
   Sub TakeOver ()
      Messagebox "Took over!"
      Call Me.bugReport.SetState (Me.bugReport.GetAssignedState())
   End Sub
   Sub SendToTesting()
      Messagebox "Unassigned reports cannot be sent to testing"
   End Sub
   Sub Complete()
      Messagebox "Unassigned reports cannot be completed"
   End Sub
End Class
Class CStateAssigned As CState
   bugReport As CStateBugReport
   Sub New (bugReport As CStateBugReport)
      Set Me.bugReport = bugReport
   End Sub
   Sub TakeOver ()
      Messagebox "Report already assigned"
   End Sub
   Sub SendToTesting()
      Messagebox "Sent to testing!"
      Call Me.bugReport.SetState
         (Me.bugReport.GetReadyForTestingState())
   End Sub
   Sub Complete()
      Messagebox "Untested reports cannot be completed"
   End Sub
End Class
Class CStateSentToTesting As CState
   bugReport As CStateBugReport
   Sub New (bugReport As CStateBugReport)
      Set Me.bugReport = bugReport
   End Sub
   Sub TakeOver ()
      Messagebox "Report already assigned"
   End Sub
   Sub SendToTesting()
      Messagebox "Report already in testing"
   End Sub
   Sub Complete()
      Messagebox "Complete!"
      Call Me.bugReport.SetState (Me.bugReport.GetCompletedState())
   End Sub
End Class
Class CStateCompleted As CState
   bugReport As CStateBugReport
   Sub New (bugReport As CStateBugReport)
      Set Me.bugReport = bugReport
   End Sub
   Sub TakeOver ()
      Messagebox "Report already completed"
   End Sub
   Sub SendToTesting()
      Messagebox "Report already completed"
   End Sub
   Sub Complete()
      Messagebox "Report already completed"
   End Sub
End Class
This is all there is to it. To test it, I wrote a simple agent that goes through states and in each new state also tests wrong transitions (e.g. from unassigned to complete etc.).
Sub Initialize Dim bugReport As New CStateBugReport() Call bugReport.SendToTesting() Call bugReport.Complete() Call bugReport.Assign() Call bugReport.Assign() Call bugReport.Complete() Call bugReport.SendToTesting() Call bugReport.Assign() Call bugReport.SendToTesting() Call bugReport.Complete() Call bugReport.Assign() Call bugReport.SendToTesting() Call bugReport.Complete() End Sub
Leave a Reply