Simplifying and Multiplying the COM Interfaces

[Previous] [Next]

We found that creating all those parsers wasn't much fun. As we developed the different parsers we said to each other, "We ought to be able to find a simpler way than developing so many parsers to take care of requirements concerning future return types." Ironically, this observation made us reconsider the last generalization step taken, the one where we went so far as to use the same interface to return XML and ADO recordsets. Before showing you the design we finally settled on, let's recapitulate the several steps we took to move from individual interfaces to highly generalized ones.

A Little Bit of History

Early in the process of designing this application, we used many different interfaces. The HorseManager class, for instance, had an IHorseMaintADO interface. The TrainerManager class had an almost identical ITrainerMaintADO interface. We showed that in Figure 9-7, and for your convenience we display a copy of it again here as Figure 11-1.

click to view at full size.

Figure 11-1. The IHorseMaintADO and ITrainerMaintADO interfaces look exactly the same.

We then decided to generalize these interfaces, combining them to make one IMaintADO interface, expecting the new interface to allow many clients to communicate data to and from the respective classes by way of ADO recordsets. Clients would have to use other interfaces to use other means of communication, and we mentioned XML data sets especially because we believe they'll play a major role in future communication and state keeping.

We did the same with the different IViewerADO interfaces, and we wound up with a design we displayed in Figure 9-11. Again, for your convenience, we show a copy of that here as Figure 11-2.

click to view at full size.

Figure 11-2. Now, after we've changed ID arguments in the interfaces into Variants, the CountryManager class can also support the interfaces.

The Present State of the Design

Combining the individualized interfaces into more general ones wasn't, however, the last step we took. We decided to generalize the interfaces even more, allowing applications to use the same interface to get XML data sets as they did for getting ADO recordsets. We displayed that last step—last until now—in Figure 9-17. Once again, we show it here as Figure 11-3 for your convenience.

click to view at full size.

Figure 11-3. The IMaint and IViewer interfaces now support different return types. They accept Variants in return, and they specify what they want using the intReturnType argument.

Now, after creating code to support this design model, we feel that the last step was a stupid one. We shouldn't have taken it. Instead, we should have insisted that the facade object choose an interface appropriate to its particular need rather than use the same interface for different needs.

Reversing that last generalization step, we found that both the facade objects and the interface event procedures in the main business classes became simpler and thus easier to create. We feel confident their quality was much higher than in the more complex versions of before.

We'll now show you some examples of the changes we made, in code as well as in model diagrams and in text. We'll cover the highlights in text, but you'll find the final version as well as scripts for setting up the SQL Server database on the companion CD-ROM that comes with this book and contains the finished application (as well as a sample database).

Simplifying the Interfaces

Our first move was to simplify the interfaces, as we just mentioned. We wanted separate interfaces for those clients designed to work with ADO recordsets and other interfaces for those designed to work with something else. Because we initially had to support only ADO recordsets, we could postpone support for XML and other transport and state-keeping means. We had to keep our main business classes open for XML—we knew, after all, that it would come and be important—but we didn't have to make provision for it just yet.

We decided to simplify the interfaces in intermediate steps. The first step was to add new interfaces to our models rather than change the ones we already had. Our plan was to generate code that made our main business classes implement additional interfaces. Then we would move code from the old interface event procedures to the new ones. We knew we had to modify that code quite a bit to take advantage of the simplification, but we still considered it worthwhile not to throw any code away just like that.

The examples we're showing you are from the HorseManager class only. We modified the other classes as well, but in the interests of time and space, we won't show you everything.

Adding New Interfaces to the Model

Adding an interface to our model, similar to an existing interface, was easy. First we added the new interface, and then we copied all the methods from the existing interface to the new one. Finally we modified each interface method by removing unwanted call arguments. We made the necessary changes to argument data types and return data types. We also modified the method comments to make them reflect the new method call syntax rather than the old.

Figure 11-4 shows how the HorseManager class now implements the new interfaces as well as the old ones. This, however, is only temporary. As soon as we've made the class fully implement the methods of the new interfaces, we'll get rid of the old ones.

click to view at full size.

Figure 11-4. The new interfaces are now added to the model and to the diagram (which isn't exactly the same thing). The HorseManager class now temporarily implements the full set.

We did the same for the other main business classes, making them implement both sets of interfaces. Then we generated code for the modified model.

Code Generated for the Interfaces Project

The first component we generated code for was the interfaces component. We had to do that since the main business entities would have to depend on the interfaces. This wasn't a very dramatic action. The only consequence—a good one—was that the two new interfaces were added to the project group.

We then generated code for the main business classes. The result of that operation was much more interesting. The first thing we noticed was that the classes (we'll focus on the HorseManager class, but it was the same with the other ones) now implemented numerous interfaces, as in the code snippet below:

'##ModelId=376A39FB0220 Implements IMaintADO '##ModelId=376A395B0356 Implements IViewerADO '##ModelId=37677DF50134 Implements IViewer '##ModelId=37677DF30004 Implements IMaint Option Explicit

Don't pay attention to the comment lines with the double lumberyards. (That's what we call the # (pound) sign1 here in Sweden.) They connect code elements to model elements. If you want to live a happy life as a developer using Rational Rose or Microsoft Visual Modeler with a language such as Microsoft Visual Basic, you should never touch these lines.

The second thing we noticed was that the code we had already entered was still there. The code generator of Rose and Visual Modeler is nondestructive. It never destroys code you have written between instances of code generation.

For instance, the IViewer_GetList method still looked like this:

Private Function IViewer_GetList(ByVal vntConditions As Variant, _ Optional intReturnType As Integer) As Variant Select Case intReturnType Case ADBReturnTypeRS ' Return a recordset. ' Get horses for name pattern. ' A later version will allow other filtering ' conditions as well. Set IViewer_GetList = GetListAsRSForNamePattern (CStr(vntConditions(0, 2))) Case ADBReturnTypeXML ' Not yet implemented Err.Raise vbObjectError, "HorseManager, IViewer_GetList", _ "XML method not yet available" Case Else ' Invalid intReturnType Err.Raise vbObjectError, "HorseManager, IViewer_GetList", _ "Return Type supplied is not valid" End Select End Function

Now we had two things to do:

  1. Given the fact that the new interfaces were simpler, we had to decide which parts of this code we still needed.
  2. Then we had to move that code to the new interface procedure.

This is what we came up with, and as you can see it is considerably simpler than the former version using the old type of super-general interface.

Private Function IViewerADO_GetList(ByVal vntConditions As Variant) _ As Recordset ' Get horses for name pattern. ' A later version will allow other filtering ' conditions as well. Set IViewerADO_GetList = GetListAsRSForNamePattern _ (CStr(vntConditions(0, 2))) End Function

What did we have to modify in the facade class? Well, not very much, as the following code segment shows. Everything we added or changed is in boldface type:

Public Function GetHorseList(ByVal strNamePattern As String) _ As Recordset Dim objHorses As HorseMaintInterfaces.IViewerADO Dim vntConditions(0, 2) As Variant vntConditions(0, 0) = "Name" vntConditions(0, 1) = "Like" If Right$(RTrim$(strNamePattern), 1) = "%" Then vntConditions(0, 2) = RTrim$(strNamePattern) Else vntConditions(0, 2) = RTrim$(strNamePattern) + "%" End If Set objHorses = CreateObject("HorseMaintEntities.HorseManager") Set GetHorseList = objHorses.GetList(vntConditions) End Function

As you can see, the only line we had to change was the one declaring the object variable that represents the HorseManager object. All we had to do was make it refer to the new interface.

Because the facade object isolates the client from the details of the main business objects, we didn't have to change the user interface or the test form at all. When we ran the client application after making these changes to the facade and main business tiers, it worked exactly as before. The only direct result of our changes was a simpler design and thus increased friendliness toward developers, increased extensibility, and increased maintainability. As it happens, we had also devised a better platform for future development.

Removing the Old Interfaces

After doing similar work with all the interfaces, which consisted of moving and modifying interface event procedures and modifying declarations in and calls from facades, it was time to remove the old interfaces from the model and then generate a new version of the code. Figure 11-5 is a Rose diagram, showing how the three main business classes share the same set of COM interfaces.

click to view at full size.

Figure 11-5. Now the main business classes share the same set of generalized interfaces, all exposing ADO recordsets for data transport.

After removing the old interfaces from the Rose model (not just from the diagram, mind you—you must remove them entirely from the model), we generated code again. Then we removed the two redundant interface classes from the Visual Basic project. We also had to do some cleaning up in the main business classes. The code generator had left some redundant comment blocks lying around—it has a tendency to do that—and we wanted to get rid of them.

After testing the new design using our test form, we decided the time we had used to make the changes was time extremely well spent. We were ready to go on to the next tier—the data access tier.



Designing for scalability with Microsoft Windows DNA
Designing for Scalability with Microsoft Windows DNA (DV-MPS Designing)
ISBN: 0735609683
EAN: 2147483647
Year: 2000
Pages: 133

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