Sample Application

[Previous] [Next]

To demonstrate the effective use of the State design pattern, I've decided to resurrect a fad that died a few years ago—the virtual pet. To better appreciate the source code implementation, I recommend that you install and run the application (VPet.exe) off the companion CD.

This virtual pet is a Chihuahua. When you run the application, you will be presented with the dialog box shown in Figure 12-4, which lets you select one of four initial states for your Chihuahua: Happy, Content, Moderate, and Discontent. Select the state you want your Chihuahua to be initially created in, and click OK.

Figure 12-4. Select the initial state for your virtual Chihuahua.

Your virtual Chihuahua window now appears, as shown in Figure 12-5. Notice the three progress bars on the right: Food Consumption, Bladder Control, and Affection Fulfillment. These progress bars represent the state of your Chihuahua. The progress bars for food and affection at 100 percent and the progress bar for bladder at 0 percent indicate a happy Chihuahua. Varying percentages in the progress bars represent your Chihuahua at different states. On the left, you'll notice a face that looks nothing like a Chihuahua. Because of time constraints (and my lack of artistic abilities), I improvised by using the yellow face icons that ship with Microsoft Visual Basic to visually represent the expressions of a Chihuahua to help reflect the mood of your virtual pet based on its current state. In addition to the yellow face expressions, there is a Label control to the right of the face that displays the current Chihuahua class of object, in this case HappyChihuahua. Below that is a TextBox control that displays textual feedback from your pet about how it's feeling. By means of a timer control, every so often the food and affection levels deplete and the bladder level increases. If you continue to neglect your Chihuahua, it will eventually become an unhappy puppy. You take care of your Chihuahua by clicking on the three buttons located below the TextBox control. The Feed button returns Food Consumption to 100 percent. The Walk button allows your Chihuahua to empty its bladder, returning the Bladder Control level to 0 percent. The Throw Frisbee button returns the Affection Fulfillment level to 100 percent based on the fact that you're playing virtual Frisbee with your Chihuahua and giving it the attention it desperately requires.

click to view at full size.

Figure 12-5. Monitor and control the health and happiness of your virtual Chihuahua.

Now that you've bonded with your new pet Chihuahua, let's take a look at the source code behind the application that makes it seem so real. Your virtual pet Chihuahua is an implementation of the State design pattern that spans a few components, as depicted in Table 12-1.

Table 12-1. Components of the Virtual Pet

ComponentsContents
Dog.tlbType library that contains definitions for the following interfaces:

IDog (State) interface is expected by the VPet application (Client).

IDogFactory interface is used instead of the New keyword to gain more control of the object creation process.

IDogStateRule interface is an abstraction of a rule used to determine state transitions.

ChihuahuaStSvr.DLLContains the IDog (State) concrete classes that represent a Chihuahua's behavior at different states.
ChihuahuaRuleSvr.DLLContains the IDogStateRule concrete classes that define the rules used in determining state transitions.
CSMSvr.DLLContains the DogStateManager (CentralStateManager) class that defines the Chihuahua state transition behavior.
DogState.mdbMicrosoft Access database that maintains a list of class names that support the interfaces IDog and IDogStateRule.
VPet.exeClient application that references objects that support the IDog interface.

The State design pattern includes these key participants:

  • IDog (State) Defines a dog interface expected by the VPet.exe application (Client). The classes that implement this interface are expected to be sensitive to state changes.
  • IDog implementation classes (ConcreteState subclasses) Defined in ChihuahuaStSvr.DLL are classes HappyChihuahua, ContentChihuahua, ModerateChihuahua, and DiscontentChihuahua that implement the IDog interface. These classes provide behavior associated with a particular mood of the Chihuahua and supply the intelligence required to collaborate with the DogStateManager (DSM) when state changes occur. For instance, examine the next code listing, extracted from the HappyChihuahua class module.
  •  ' HappyChihuahua.cls Implements DogLib.IDog ' Central State Manager Private m_dsm As CSMSvr.DogStateManager  ' IDog.Affection Private Property Get IDog_Affection() As Integer IDog_Affection = m_dsm.Affection End Property Private Property Let IDog_Affection(RHS As Integer) m_dsm.Affection = RHS End Property ' IDog.Bark() Private Function IDog_Bark() As String Dim theDog As DogLib.IDog m_sNewClassName = m_dsm.getDogObject(m_sThisClassName, _ m_DelegateDog) If m_sThisClassName <> m_sNewClassName Then IDog_Bark = m_DelegateDog.Bark Else If m_nIndex < 2 Then m_nIndex = m_nIndex + 1 Else m_nIndex = 0 End If IDog_Bark = m_Barks(m_nIndex) End If End Function 

    Notice the HappyChihuahua class defers all read/write property value operations to the DSM referenced by m_dsm, as demonstrated in the Property Let and Property Get procedures for the Affection property. Property values represent the state. The effects of state changes are apparent when IDog methods are invoked, as illustrated in the HappyChihuahua implementation of the IDog.Bark method. To dynamically determine the appropriate bark behavior, the DSM is queried for a reference to the correct IDog implementation (object that implements IDog), which is returned in the m_DelegateDog argument. The DSM also returns the class name of the object that defines the behavior to be employed in the m_sThisClassName argument. If the class name is the same as the class that made the call to the DSM, the caller's implementation is executed; otherwise the implementation is delegated to the object reference returned by the DSM (m_sNewClassName). Regardless of the specific property or method implementation, you should consider the collaboration with the DSM as the State class implementation template and reapply it in all classes that support the IDog interface.

  • DogStateManager (CentralStateManager) A class that serves as the central state repository for all IDog implementation classes. It also defines state transition behavior that involves returning the appropriate IDog implementation class object associated with the current state. To make for a more elegant solution, I decided to make not only the variety of IDog implementations but also the state transition rules dynamically configurable from a database. I accomplish this by defining the StateTrans table in the DogState Access database and including Rule and TrueOutcome fields that store IDogStateRule and IDogFactory implementation class names, respectively. (See Figure 12-6.) In the DogStateManager.Init method, a query is run against the StateTrans table to create IDogStateRule and IDogFactory objects. IDogStateRule objects and IDog objects created via IDogFactory objects are cached in arrays in the order in which the rules will be evaluated as determined by the EvalOrder field in the StateTrans table. In the DogStateManager.getDogObject method, the state transition algorithm, by means of a For loop, traverses the array of IDogStateRule objects from 0 to n. The first rule that evaluates to True results in exiting the For loop after setting the reference to the IDog implementation that corresponds to the rule. Implementing the DogStateManager is the most complex part of the State design pattern, but it has to be written only once. Adding new rules and state objects (IDog implementations) can be done without disruption to this code.
  • click to view at full size.

    Figure 12-6. StateTrans table that primarily stores the IDogStateRule and IDog-Factory class names used by the DogStateManager to dynamically configure itself for participation in the State design pattern.

     ' DogStateManager.cls Private m_sClassName As String Private m_iAffection As Integer  Private m_bIsDirty As Boolean Private m_rsDogClassInfo As ADODB.Recordset Private m_Connection As ADODB.Connection Private m_Rule() As IDogStateRule Private m_Dog() As IDog Public Function Init(ClassName As String) As Boolean Dim df As DogLib.IDogFactory Dim sConnect As String Dim sUID As String Dim sqlDogInfo As String Dim nRecCount As Integer Dim i As Integer sqlDogInfo = "SELECT Rule, TrueOutCome " & _ "FROM StateTrans " & _ "WHERE ClassName='Chihuahua' " & _ "ORDER BY EvalOrder;" sUID = "Admin" sConnect = "Provider=Microsoft.Jet.OLEDB.4.0; " & _ "Data Source=" & App.Path & "\DogState.mdb" ' Establish connection. m_Connection.Open sConnect, sUID If m_Connection.State <> adStateOpen Then Init = False Exit Function End If ' Retrieve Rule and State class names from DogState ' Access database. m_rsDogClassInfo.Open sqlDogInfo, m_Connection, _ adOpenStatic nRecCount = m_rsDogClassInfo.RecordCount If nRecCount = 0 Then m_rsDogClassInfo.Close Init = False Exit Function End If ' Cache Rule objects and Dog (State) objects in arrays. ReDim m_Rule(0 To nRecCount - 1) As IDogStateRule ReDim m_Dog(0 To nRecCount - 1) As IDog i = 0 m_rsDogClassInfo.MoveFirst Do While Not m_rsDogClassInfo.EOF Set m_Rule(i) = _ CreateObject(m_rsDogClassInfo("Rule")) Set df = _ CreateObject(m_rsDogClassInfo("TrueOutcome")) Set m_Dog(i) = df.CreateInstance(False) m_rsDogClassInfo.MoveNext i = i + 1 Loop m_rsDogClassInfo.Close Init = True End Function Public Property Get Affection() As Integer Affection = m_iAffection End Property Public Property Let Affection(RHS As Integer) ' Store property value and set dirty flag to true to ' indicate to the state transition algorithm that it needs ' to reevaluate.  m_iAffection = RHS m_bIsDirty = True  End Property Public Function getDogObject(inCurrentDogClass As String, _ outDogObject As IDog) As String ' Employs state transition algorithm to determine the ' reference to the appropriate IDog object to return to the ' outDogObject object variable. Dim i As Integer ' Perform state transition algorithm only if any ' property values have been changed. If m_bIsDirty = True Then For i = 0 To UBound(m_Rule) If m_Rule(i).Evaluate(m_iAffection, _ m_iBladderLevel, _ m_iFoodConsumption) = True Then m_sClassName = TypeName(m_Dog(i)) If m_sClassName <> inCurrentDogClass Then Set outDogObject = m_Dog(i) End If Exit For End If Next i m_bIsDirty = False End If If Len(m_sClassName) = 0 Then getDogObject = inCurrentDogClass Else getDogObject = m_sClassName End If End Function 



Microsoft Visual Basic Design Patterns
Microsoft Visual Basic Design Patterns (Microsoft Professional Series)
ISBN: B00006L567
EAN: N/A
Year: 2000
Pages: 148

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net