cFactory Interface
Member | Description |
Create | Initializes the business object with a DAL object |
Populate | Creates worker objects according to any passed-in parameter object |
Item | Returns a worker object |
Count | Returns the number of worker objects contained in the factory |
Add | Adds an existing worker object to the factory |
AddNew | Returns a new empty worker object |
Persist | Updates all changes in the worker objects to the database |
Remove | Removes a worker object from the factory |
Recordset | Returns the internal factory recordset |
Delete | Deletes a worker object from the data source and the factory |
Parameters | Returns the parameters that can be used to create worker objects |
Creating a factory object
Creating the factory is simple. The code below demonstrates the use of the Create method to instantiate a factory object. This code exists in the action objects (which we'll cover later). This sample action object uses the Employees business object to perform a business process.
Dim ocDAL As New cDAL Dim ocfEmployees As New cfEmployees ocfEmployees.Create ocDAL
Hold it-what's the DAL doing here? Isn't the purpose of business objects to remove the dependency on the DAL? Yes, it is. But something important is happening here, and it has everything to do with transactions. The scope and lifetime of the action object determines the scope and lifetime of our transaction for the business objects as well.
Say our action object needs to access four different factory objects and change data in each of them. Somehow each business object needs to be contained in the same transaction. This is achieved by having the action object instantiate the DAL, activating the transaction and passing that DAL to all four factory objects it creates. This way, all our worker object activities inside the action object can be safely contained in a transaction, if required. More on action objects and transactions later.
So what does the Create code look like? Too easy:
Public Sub Create(i_ocDAL As cDAL) Set oPicDAL = i_ocDAL ' Set a module-level reference to the DAL. End Sub
Populating a factory object
Creating a factory object isn't really that exciting-the fun part is populating this factory with worker objects, or at least looking like we're doing so!
Dim ocDAL As New cDAL Dim ofcEmployees As New cfEmployees Dim ocParams As New cParams ofcEmployees.Create oDAL ocParams.Add "Department", "Engineering" ofcEmployees.Populate ocParams
Here a recordset has been defined in the DAL as Employees, which can take the parameter Department to retrieve all employees for a particular department. Good old cParams is used to send parameters to the factory object, just like it does with the DAL. What a chipper little class it is!
So there you have it-the factory object now contains all the worker objects ready for us to use. But how does this Populate method work?
Private oPicRecordset As cRecordset Public Sub Populate (Optional i_ocParams as cParams) Set oPicRecordset = oPicDAL.OpenRecordset("Employees", i_ocParams) End Sub
The important point here is that the Populate method is only retrieving the recordset-it is not creating the worker objects. Creating the worker objects is left for when the user accesses the worker objects via either the Item or Count method.
Some readers might argue that using cParams instead of explicit parameters detracts from the design. The downside of using cParams is that the parameters cannot be determined for this class at design time and do not contribute to the self-documenting properties of components. In a way I agree, but using explicit parameters also has its limitations.
The reason I tend to use cParams rather than explicit parameters in the factory object Populate method is that the interface to the factory class is inherently stable. With cParams all factory objects have the same interface, so if parameters for the underlying data source change (as we all know they do in the real world) the public interface of our components will not be affected, thereby limiting the dreaded Visual Basic nightmare of incompatible components.
Also of interest in the Populate method is that the cParams object is optional. A Populate method that happens without a set of cParams is determined to be the default Populate method and in most cases will retrieve all appropriate objects for that factory. This functionality is implemented in the DAL.
Obtaining a worker object
After we have populated the factory object, we can retrieve worker objects via the Item method as shown here:
Dim ocDAL As New cDAL Dim ofcEmployees As New cfEmployees Dim owcEmployee As New cwEmployee Dim ocParams As New cParams ofcEmployees.Create ocDAL ocParams.Add "Department", "Engineering" ofcEmployees.Populate ocParams Set owcEmployee = ofcEmployees.Item("EdmundsM") MsgBox owcEmployee.Department
At this point, the Item method will initiate the instantiation of worker objects. (Nothing like a bit of instantiation initiation.)
Public Property Get Item(i_vKey As Variant) As cwEmployee If colPicwEmployee Is Nothing Then PiCreateWorkerObjects Set Item = colPicwEmployee(i_vKey).Item End Property
So what does PiCreateWorkerObjects do?
Private Sub PiCreateWorkerObjects Dim owcEmployee As cwEmployee Do Until oPicRecordset.EOF Set owcEmployee = New cwEmployee owcEmployee.Create oPicRecordset, oPicDAL oPicRecordset.MoveNext Loop End Sub
Here we can see the payback in performance for using the Shared Recordset Model. Initializing the worker object simply involves calling the Create method of the worker and passing in a reference to oPicRecordset and oPicDAL. The receiving worker object will store the current row reference and use this to retrieve its data.
But why is the DAL reference there? The DAL reference is needed so that a worker object has the ability to create a factory of its own. This is the way object model hierarchies are built up. (More on this later.)
The Item method is also the default method of the class, enabling us to use the coding-friendly syntax of
ocfEmployees("637").Name
Counting the worker objects
Couldn't be simpler:
MsgBox CStr(ofcEmployees.Count) Private Property Get Count() As Long Count = colPicfEmployees.Count End Property
Says it all, really.
Adding workers to factories
Often you will have a factory object to which you would like to add pre-existing worker objects. You can achieve this by using the Add method. Sounds simple, but there are some subtle implications when using the Shared Recordset implementation. Here it is in action:
Dim ocDAL As New cDAL Dim ocfEmployees As New cfEmployees Dim ocwEmployee As New cwEmployee ocfEmployees.Create ocDAL Set ocwEmployee = MagicEmployeeCreationFunction() ocfEmployees.Add ocwEmployee
You'll run into a few interesting quirks when adding another object. First, since the worker object we're adding to our factory has its data stored in another factory somewhere, we need to create a new row in our factory's recordset and copy the data from the worker object into the new row. Then we need to set this new object's recordset reference from its old parent factory to the new parent factory, otherwise it would be living in one factory but referencing data in another-that would be very bad. To set the new reference, we must call the worker Create method to "bed" it into its new home.
Public Sub Add(i_ocwEmployee As cwEmployee) oPicRecordset.AddNew With oPicRecordset.Fields .("ID") = i_ocwEmployee.ID .("Department") = i_ocwEmployee.Department .("FirstName") = i_ocwEmployee.FirstName .("LastName") = i_ocwEmployee.LastName End With oPicRecordset.Update i ocwEmployee.Create oPicRecordset End Sub
And there you have it-one worker object in a new factory.