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