I have enhanced the off-line banking system sample application (BSTAM Banker) from the Object By Value design pattern (Chapter 6) to include the Repository design pattern. To summarize, the BSTAM Banker allows bank customers to perform an extensive list of banking services (such as depositing and withdrawing funds and checking the balance) against their accounts off-line. The benefits are mainly optimal system performance without the loss of functionality. Why shouldn't you have your cake and eat it too? In the Chapter 6 sample, customer savings accounts are maintained in a Scripting.Dictionary object by the BankSvr.Teller object. This requires the Teller object to have intimate knowledge about how to manipulate the Dictionary object. What if later you decide to store the accounts in a Sybase database? The Teller object's implementation would have to be rewritten using an appropriate Sybase database API.
Let's say, for example, you decide to use ADO. Manipulating data using ADO is not even remotely similar to using a Dictionary object. Sounds like a big change? Well it is. To remove from the business object layer the responsibility, complexity, and limitations associated with data manipulation in a specific type of data store, in this example I've replaced the Dictionary object with an implementation of the Repository design pattern. Since the SavingsAccount objects are persistable, half the design pattern is already implemented. All that is required is a Repository object. Hence this sample is an ideal fit. Finally, a new feature not found in the BSTAM Banker example in Chapter 6 has been added to allow a customer to update his or her account with the changes made off-line. Account updates are ultimately stored in the Repository referenced by the Teller object. Since BSTAM Banker is already explained in great detail in Chapter 6, here I'll concentrate solely on the Repository design pattern implementation. For a complete source code disclosure, refer to the companion CD.
The key participants of the sample that make up the Repository design pattern include the following:
Private Sub Class_ReadProperties(PropBag As PropertyBag) ' Read in property values from a byte array via PropertyBag ' object. With PropBag m_strAccountNo = .ReadProperty("AccountNumber") m_strAccountOwner = .ReadProperty("AccountOwner") m_dblInterestRate = .ReadProperty("InterestRate") m_dblBalance = .ReadProperty("AccountBalance") End With End Sub Private Sub Class_WriteProperties(PropBag As PropertyBag) ' Write out property values to a byte array via PropertyBag ' object. With PropBag .WriteProperty "AccountNumber", m_strAccountNo .WriteProperty "AccountOwner", m_strAccountOwner .WriteProperty "InterestRate", m_dblInterestRate .WriteProperty "AccountBalance", m_dblBalance End With End Sub |
' Class Name: Repository ' Note: Repository interface that defines the methods for ' storing and retrieving a persistable object to and ' from an underlying data store ' Option Explicit Public Function Add(PID As String, Obj As Object) As Boolean End Function Public Function Update(PID As String, Obj As Object) _ As Boolean End Function Public Function Remove(PID As String) As Boolean End Function Public Function Retrieve(PID As String) As Object End Function |
' Class Name: InMemRepository ' Note: A concrete class that implements an in-memory data ' store using a Scripting.Dictionary object ' Implements Repository Private m_ObjectStates As Scripting.Dictionary ' Declared in the Globals.bas file. ' Transformer facilitates the persistence of an object's ' state to a byte array data stream stored in a PropertyBag ' object, which is explained in complete detail in the ' Implementation section of Chapter 6. Public g_Transformer As ObjTransformLib.Transformer Private Function Repository_Add(PID As String, _ Obj As Object) As Boolean ' Adds a persistable object's state to the data store. Dim byteArr() As Byte If Not m_ObjectStates.Exists(PID) Then ' This code must be implemented if you don't ' use the Transformer class from Chapter 6. ' ' Dim pb As PropertyBag ' Set pb = New PropertyBag ' pb.WriteProperty PID, obj ' byteArr = pb.Contents byteArr = g_Transformer.ObjectToStream(PID, Obj) m_ObjectStates.Add PID, byteArr Repository_Add = True Else Repository_Add = False End If End Function Private Function Repository_Retrieve(PID As String) _ As Object ' Creates a persistable object, reinitializes its state from ' the data store, and returns a reference to the object Dim vTemp As Variant Dim byteArr() As Byte If m_ObjectStates.Exists(PID) Then vTemp = m_ObjectStates(PID) byteArr = vTemp ' This code must be implemented if you don't ' use the Transformer class from Chapter 6. ' ' Dim pb As PropertyBag ' Set pb = New PropertyBag ' pb.Contents = byteArr ' Set Repository_Retrieve = pb.ReadProperty(PID) Set Repository_Retrieve = g_Transformer _ .StreamToObject(PID, byteArr) Else Set Repository_Retrieve = Nothing End If End Function |
' Teller.cls ' Private Sub Class_Initialize() Dim sa As AccountLib.SavingsAccount Dim bRetcode As Boolean ' Declared as a member variable in Declarations section ' Private m_ObjectRep As RepoLib.Repository ' Construct in-memory repository Set m_ObjectRep = New RepoLib.InMemRepository ' Construct and initialize SavingsAccount objects, and ' store in in-memory repository. ' Maxwell Orion's account Set sa = New AccountLib.SavingsAccount With sa .AccountNumber = "orion0425" .AccountOwner = "Maxwell Orion" .InterestRate = 0.039 .Deposit 30000# End With ' Add Maxwell's savings account object to in-memory ' repository. bRetcode = m_ObjectRep.Add(sa.AccountNumber, sa) |
When a client requests a savings account, the Teller object retrieves the savings account object from the in-memory repository.
' Teller.cls ' Public Function RetrieveAccount(AcctNo As String, ...) _ As Boolean Dim sa As AccountLib.SavingsAccount ' Declared as a member variable in Declarations section ' Private m_ObjectRep As RepoLib.Repository ' Retrieve a SavingsAccount object from the ' SavingsAccount object repository (m_ObjectRep). Set sa = Nothing Set sa = m_ObjectRep.Retrieve(AcctNo) |
When a client updates a savings account, the Teller object stores the state of the savings account object to the in-memory repository:
' Teller.cls ' Public Function UpdateAccount(AcctNo As String, ...) _ As Boolean Dim sa As AccountLib.SavingsAccount ' Declared as a member variable in Declarations section ' Private m_ObjectRep As RepoLib.Repository UpdateAccount = m_ObjectRep.Update(AcctNo, sa) End Function |