Controlling Connections

[Previous] [Next]

We firmly believe that you're much better off being the one to control how facade objects should be allowed to connect to main business objects. We can think of several reasons why you should be in control.

One of the most important reasons is to enhance life expectancy. We don't mean your life expectancy—rather, consider how long you expect your components to live. If you wish them a long life, you should definitely create separate interfaces for them, defining all their internal methods, events, and properties as private to them. Why will this increase their life expectancy? We'll explain, using an example in which we assume that you're in the process of creating an application without using Extensible Markup Language (XML). Furthermore, we also assume that you believe that other client applications, in time, will need to have your server objects return their results in the form of XML. In other words, you need to make allowance for future requirements as well as for current ones.

A Very Likely Scenario

Let's assume the following:

  • A version of your server application exists already. It has a HorseManager class. Most of the methods of this class use ADO recordsets to transport data.
  • One user application depends on your server application in general and, for our discussion, on your HorseManager class in particular. This application is a tool for office administrators responsible for maintaining the horse racing data resource.
  • Other applications that depend on the HorseManager class might also exist. Since this class is contained in a published component available to many developers, such applications can very well exist without you or your department knowing about them. Such reuse, after all, is one of the things you strive for when you start building components.
  • Anyway, since such applications might exist, you're not allowed to change the public interface of the HorseManager class. This is true even if you and your organization would modify the maintenance application together with the HorseManager class. If you change a public interface of a class, you run the risk of ruining other applications that also use it and depend on it to stay just the way it is now.
  • Now let's look forward a bit. About a year from now, a new user application comes under development. This application is open to customers, allowing them to browse the racing data resource in order to improve their gambling. Since Microsoft and other IT vendors have—at least according to our scenario—released good support for XML, your company's management has decided that the new application should receive XML data sets rather than ADO recordsets.

One way to solve this problem would be to create a second class, HorseManagerXML, shown in Figure 9-1. This class would support the new application, whereas the old HorseManager class would continue to support all the applications that already existed at the time. Then you would have two HorseManager classes, and much of their code would be the same.

What to Do When XML Arrives in a Big Way

In our examples so far, all the objects we've defined transport data in ADO recordsets. Say that you made a non-XML choice for an application you were developing in the early spring of 1999. At the end of the year 2000, you might very well prefer using XML for similar tasks.

As good tools in managing XML data sets become available, developers will increasingly want to use them for transporting data between processes, computers, and even companies. Since XML is an emerging Web standard, you should be able to send XML data sets to far more places than will ever be available to ADO recordsets.

This will happen fast. Already today, Windows 2000 has a new version of ADO. This version—ADO 2.5—allows recordsets to be converted to XML documents, which you can think of as XML data sets, and then converted back again for database modifications.

"So what?" you might say. "ADO 2.1 allows me to do that. What's so different about 2.5?"

Well, quite a lot:

  • Whereas ADO 2.1 can convert recordsets to XML only by saving them to a file, ADO 2.5 lets you save the entire recordset into an object or a variable.
  • ADO 2.1 can't translate hierarchical recordsets to XML. ADO 2.5 can do that and also translate such complex data sets back into hierarchical ADO recordsets again.

If you're going to stay in the development business, XML is going to mean a lot to you.

Figure 9-1. Since clients depend directly on objects, you need a new object when new requirements surface.

A simple change in requirements

Let's assume for a moment that you decide that this redundancy is bearable. After all, having two HorseManager classes isn't the end of the world. You begin to design and implement the new class, cutting and pasting a lot. Sometimes you'll even have the old and the new HorseManager classes call each other to reduce redundancy. You don't feel your best when you add this complexity to your application, but you'd feel even unhappier to double the code. Eventually you release the new application.

As time goes by, new applications need to be developed. Since horses are so central to horse racing, it's unavoidable that some of these applications will need to use the HorseManager classes, or at least one of them. The new applications probably need to use some existing functionality, but sometimes they might also need new or changed functionality. Say, for instance, that one of the new applications lets users select horses for a list not only by a name pattern but also by the sex of the horses. For example, a user asks for a list of all the fillies, and fillies only, with names starting with "Ar." (Such a list, by the way, would include Artemis Cup, the 1999 winner of Dianalöpning, which is the Swedish 1000 Guineas classic race.)

Since you can't change the interface of any of the existing classes, you might wind up with two new classes, as Figure 9-2 indicates. The only difference between the new classes and the old ones is the second argument of the GetList method.

Now you're almost bound to have either a lot of redundant code or too much interaction between these classes. You have also increased the maintenance burden and the level of risk in modifying any of the methods of these classes. Every time a change in requirements calls for such a change, you really need to consider how many classes the change will affect—is it one, two, three, or even more?

click to view at full size.

Figure 9-2. A very small requirement change doubled the number of classes, with a lot of redundant code as a result.

A Better Solution—Separate COM+ Interfaces

COM and COM+ offer a better solution to the new requirement problem. A single class can expose several different interfaces. Rather than define a new class when new requirements surface, you can define a new COM+ interface to the existing class and add support for it in the class. But this isn't the only benefit of using separate COM+ interfaces when connecting to a COM+ object. You can use different interfaces to the same object for different categories of users. Figure 9-3 shows how one user category—internal administrators—can use one interface to the HorseManager class for maintenance purposes while another category—customers—can use another interface to browse through race information.

click to view at full size.

Figure 9-3. The HorseManager class now supports two separate interfaces—one to do maintenance and another through which to view data on horses. Note that there is some redundancy in the interfaces but none in the class itself.

In Figure 9-3, a single HorseManager class implements two separate interfaces:

  • IHorseMaintADO, which allows the original client application to maintain data on horses. It returns ADO recordsets from fetching operations and receives ADO recordsets for modification of the data resource.
  • IHorseViewerADO, which allows customers to view data on horses. Like IHorseMaintADO, it returns data in the form of ADO recordsets. Since it doesn't expose any methods that modify the data resource, there's no need to send it any recordsets.

The purpose of these two interfaces is to separate different concerns from each other. So far the interfaces have nothing to do with the redundancy of code that we mentioned only a minute ago, but they're good examples of the high value of separate interfaces just the same. Right now we'll talk about how the use of separate interfaces can help you achieve a long life for your components and at the same time reduce code redundancy, but after that—in this very chapter—we aim to come back to the idea presented here of using separate interfaces for different concerns of different user categories. We'll also talk about how you can control permission to each COM+ interface, thus controlling who is allowed to do what with a business object.

Reducing Code Redundancy

At compile time, Visual Basic generates a default interface for any class belonging to an ActiveX DLL or ActiveX EXE project. If a client of such a class doesn't explicitly specify another interface, COM+ automatically gives the client access to the object through its default interface. If you don't want that to happen, you must make it impossible. Let's see how.

Make the implementation methods private

You should expose all access to the objects of a class through separate interfaces. The methods exposed through these interfaces should be public, allowing clients to access the object through them; the methods inside the class that implement these interfaces should be private to the class. The advantage of this practice is in its allowing you to modify the code of the class as much as you want, as long as you continue to honor every interface it implements. In effect, you reserve ownership of the implementations of the interfaces to yourself and your organization, whereas you transfer ownership of the interfaces to the public.

Accordingly, you should make all the methods, properties, and events of the class itself private so that nobody can access them without using the public interfaces. Figure 9-4 displays a new version of the Rose diagram. It features two separate public interfaces to a public class, even though the class has no public methods. All methods are private to the class, available only through the two public interfaces. (As usual in Rational Rose and Microsoft Visual Modeler diagrams, a padlock to the left of the name of a method, a property, or an event indicates that it's private to the class.)

click to view at full size.

Figure 9-4. The methods that implement the different operations are now private to the class. Interfaces are public—implementation is private.

New interfaces for XML transport

When you need to use XML data transport, all you have to do is add new interfaces to the class and make sure that the class supports these interfaces without interfering with the old ones. Figure 9-5 shows the enhanced model, now featuring four different interfaces instead of two, but still with one class only.

You might be saying to yourself, "We had four different classes—now we have one class and four different interfaces. Where's the profit in this exchange?" Our big achievement is that we've kept our code in one class only. An interface, by definition, is completely without code.

Since all the methods (there are no properties or events—only methods) of our HorseManager class are private, they're all yours to modify any way you please. The only restriction is that all the interfaces must be correctly (and preferably efficiently) supported.

Now, after modifying the server application in accordance with Figure 9-5, old clients can use the original IHorseMaintADO interface from Figure 9-4 to receive data in the form of ADO recordsets, while new clients can use the new IHorseMaintXML interface to receive and deliver data in the form of XML data sets. Furthermore, customer applications can select either the IHorseViewerADO or IHorseViewerXML interface. COM+ and Microsoft Transaction Server (MTS) use the role concept, described later in this chapter, to decide who will have access to the functionality of the class, and they do it in the same way.

click to view at full size.

Figure 9-5. A later version of the same class supports additional interfaces for additional purposes. Still, only one HorseManager class implements these interfaces.

You Can Change Implementation at Will

But we have one problem with the design presented in Figure 9-5. In the class itself, the private GetList method still returns only an ADO recordset. If you study the diagram, you'll find that the same goes for all the other methods as well. The only exception is the Delete operation, which is supposed to work the same way whether you're using the ADO or the XML version of the interfaces. In other words, the class itself doesn't yet support the new interfaces.

But as we've said perhaps too may times now, one of the advantages of a structure such as the one in Figure 9-5 is that you, the developer of the class, own the code. You're completely free to change the signature of every private method as much as you like to achieve support for all the interfaces. The same, of course, goes for the code that implements the methods.

Figure 9-6 shows a new version of the diagram, in which each method now returns (or accepts) Variants instead of ADO recordsets. The content of a Variant can be a string containing an entire XML document, an ADO recordset, or just about anything you would like to put in the variable. So if a method capable of returning a Variant can choose to return any kind of value, our Variant-returning methods can choose to return either an ADO recordset or a string containing an XML document. The question is how the class decides which type of value to return.

click to view at full size.

Figure 9-6. The implementation is now changed, allowing the different operations to return recordsets or XML and also to accept recordsets or XML as arguments. Also notice that the GetEmptyRecordset method still returns recordsets only. You don't need this operation if you're using XML.

Take a new look at the methods of the HorseManager class in Figure 9-6. Let's concentrate on the GetById method. You can see that it has an argument it didn't have before. The intReturnType parameter tells the method whether to return a recordset or an XML document.

Let's take a look at a few code snippets that demonstrate more clearly how this will work. In each snippet, the parts of interest are printed in boldface type. The first block of code shows the interface event that's automatically called when someone calls the GetById method through the IHorseMaintXML interface:

Private Function IHorseMaintXML_GetById (lngId As Long) As String Dim strXML As String strXML = GetById(lngId, RetXML) IHorseMaintXML_GetById = strXML End Function

The most interesting part of this code snippet is the RetXML constant (assumed to be defined and also understood by the function). It tells the private GetById method what to return.

The next block of code, which implements the ADO version of the GetById method, is almost identical to the first one. The only differences are those necessary for returning an ADO recordset rather than an XML document:

Private Function IHorseMaintADO_GetById (lngId As Long) As Recordset Dim rs As Recordset Set rs = GetById(lngId, RetADO) Set IHorseMaintADO_GetById = rs End Function

The final code snippet shows the private GetById function, which must be able to return either an XML document or an ADO recordset. It uses the second argument to make that choice:

Private Function GetById (lngId As Long, intReturnType As Integer) _ As Variant Dim strXML As String, rs As Recordset  ' Code that gets the recordset Set rs = ... Select Case intReturnType Case RetXML ' Code that converts ADO to XML  GetById = strXML Case RetADO Set GetById = rs End Select End Function



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