We'll go through this one a little faster.
Let's start with the facade class, for which we now have test stub code. The first thing we'll have to do is move that code to the main business object. After doing that, we can implement the facade method for real, making it call the main business object. Here's the new code in the facade:
Public Function GetTrainerList _ (Optional ByVal strLastNamePattern As String) As Recordset Dim objTrainers As HorseMaintInterfaces.IViewer Dim vntConditions As Variant Set objTrainers = _ CreateObject("HorseMaintEntities.TrainerManager") Set GetTrainerList = objTrainers.GetList(vntConditions) 'NOTE: the vntConditions variable is empty! End Function |
As you can see, it's almost a copy of the GetCountryList method in the same facade. The only difference is that we ask for a trainer list instead of a country list. Naturally, we communicate with a TrainerManager object instead of a CountryManager object.
You might notice that we even use the same interface for getting the list, the generalized IViewer interface. This interface needs a vntConditions argument to help select the proper set of trainers. In our case we want them all, so we leave the vntConditions argument empty. Since this is the default state of a Variant variable, we don't have to do anything with this variable except declare it.
The facade object will call the main business object through the IViewer interface, so we'd better start there.
In the code that follows, we use a Case statement. It separates different return types from one another. Since our client (see the preceding code) wants a recordset, we specify ADBReturnTypeRS. This takes our code into the first leg of the Case structure. There we test for the default condition, which is an empty vntConditions argument. After finding it, we're ready to call the GetListAsRSForNamePattern method, as shown in the boldface part of the following code.
Private Function IViewer_GetList(ByVal vntConditions As Variant, _ Optional intReturnType As Integer) As Variant Select Case intReturnType Case ADBReturnTypeRS ' Return a recordset If IsEmpty(vntConditions) Then ' Get all trainers Set IViewer_GetList = GetListAsRSForNamePattern() Else ' Not yet implemented Err.Raise vbObjectError, "TrainerManager, IViewer_GetList", _ "Filtered Recordset method not yet available" End If Case ADBReturnTypeXML ' Not yet implemented Err.Raise vbObjectError, "TrainerManager, IViewer_GetList", _ "XML method not yet available" Case Else ' Invalid intReturnType Err.Raise vbObjectError, "TrainerManager, IViewer_GetList", _ "Return type supplied is not valid" End Select End Function |
You should note that the IViewer_GetList method is private to the class. It specifies how objects of this class should behave when they receive GetList method calls through the IViewer interface. In the preceding code, you have implemented only part of this behavior. You are, however, free to enhance this code to do more than it does now. Here are a few examples of what you can do without breaking any contracts with client applications:
The important thing is that this interface allows enhancements. A Variant argument for filtering conditions is very useful because it can take many forms. A Variant variable can be a single value, an array of values, an array of records, a recordset, and even an array of recordsets. Flexibility is probably the most distinguishing quality of Variant variables.
The GetListAsRSForNamePattern method will get the trainer list from the database. It will delegate this task to a data access object, but nonetheless it will be responsible for the job within this class.
So naturally we moved the test stub code from the facade class to here. After moving it, we had to modify only the boldface part of the code, and here it is:
Private Function GetListAsRSForNamePattern _ (Optional ByVal strLastNamePattern As String) As Recordset Dim rs As ADODB.Recordset Set rs = CreateObject("ADOR.Recordset") rs.Fields.Append "TrainerId", adInteger, 4 rs.Fields.Append "TrainerName", adVarChar, 40 rs.Open rs.AddNew rs!TrainerId = 16 rs!TrainerName = "Kahn, Michael" rs.AddNew rs!TrainerId = 20 rs!TrainerName = "Gustafsson, Tommy" Set GetListAsRSForNamePattern = rs End Function |
Now the facade object calls the TrainerManager object through the IViewer interface. The call works, and the preceding test stub code will execute to return an artificial recordset, just as the facade previously did.
To the test form and for the GUI prototype, everything looks the same as before, but we have made good progress just the same.
Why the CreateObject Function and Not the New Keyword?You might wonder why we use CreateObject rather than New, even when we create ADO Recordset objects. After all, they won't run under the control of COM+ or MTS. The reason is simple: the CreateObject function is entirely COM compliant, whereas the New keyword is not. In many cases, it doesn't matter whether you use CreateObject or New, while in other cases it's critical that you use CreateObject. When you're creating ADO recordsets, it really doesn't matter which you use.
We still prefer to use CreateObject. Some would argue that New is faster; we would answer, "Yes, but in this case not by much." If you set your mind to always use CreateObject, you'll help yourself avoid using New when you ought to have used CreateObject.