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