Delegating to Entity and Set Classes

[Previous] [Next]

Equipped with a complete list of the methods needed to implement the Maintain Horses use case, introduced as early as Chapter 2, "Designing for Scalability," you can now begin to delegate operations to entity and set management classes. These types of classes are described in Chapter 1, "Planning for a New Architecture," and you might recall that an entity class represents one single entity, such as a horse, and that a set management class manages sets or collections of entity classes. The best strategy is to do this one method at a time.

Delegating the GetHorseList Method

Using the GetHorseList method as our first example, Figure 8-2 shows that the facade class delegates the work this method exposes to a corresponding method in the Horses class. Since the Horses class didn't exist in the model, we had to create it. Therefore, the GetList method is the first and so far the only one for the class.

click to view at full size.

Figure 8-2. An fcdHorseMaint object uses a Horses object's GetList method to get a list of horses.

As you can see in the figure, the argument used to call both methods is the same. Any name pattern received by the facade class method will be transferred to the method in the set management class.

Dependency rather than association

The broken arrow between the two classes in Figure 8-2 indicates that we've made the relationship a dependency rather than an association. A solid arrow would indicate an association. This distinction is important.

An association means that the associated object shares its life cycle with the one associating. This means that had we made the relationship an association rather than a dependency, the Horses object would live just as long as the fcdHorseMaint object. (You can use programming to change this, making the life cycle of the associated object shorter, but this shared life cycle is essentially what an association means.)

We don't want the Horses object to live as long as the facade object does. Why not? Because an application built on such a principle isn't scalable. In such an application, the Horses object would exist even when it wasn't needed, consuming server resources. What we really want is to have the Horses object activated as late as possible to conserve resources. For the same reason, we want it to deactivate itself as soon as it has done its job. This is why we have chosen a dependency rather than an association. Rational Rose and Microsoft Visual Modeler don't generate any code at all for a dependency. We have to do it ourselves in our Visual Basic project, which allows us to locate the Horses object where we want it—inside a method definition. By placing the declaration inside a method definition, we cause the variable, or rather the object the variable refers to, to be activated inside the method call and deactivated upon its completion. This is exactly what we want. The following code snippet is an example of a dependency declared within a method:

Public Sub GetHorseList (strNamePattern As String) As Recordset  Dim objHorses As EntityProject.Horses  End Sub

If we had used an association instead of a dependency relationship, Rose and Visual Modeler would have generated a module-level variable declaration, as in the following code snippet:

Private objHorses As EntityProject.Horses

With such a declaration, you as a developer must be very careful, making sure that every time you use objHorses its life cycle is as short as possible.

Delegating the GetHorseById Method

The first method we looked at, GetHorseList, is a set-oriented method. Its purpose is to collect a set of horse names rather than information about a single horse. The next method we examine is different. The GetHorseById method fetches all available data about a single horse—the one that can be identified by a given ID.

You already have a class for horse-related information and operations: horses. The question now is whether to delegate individually oriented operations—such as GetHorseById—to that class or to create a special class for these operations. After all, the name of the class is plural, and the only method we have in it so far—GetList—fetches a set of horses, which is also plural.

If you could get away with one class only for horse operations, your design would be simpler and easier to manage. If, however, you select that road and then—after deployment—find out that you really should have opted for two separate classes, you're in trouble. In our opinion, this is really a question of state keeping. If there's a need for stateful Horse objects, now or later in the lifetime of the application, you should have two different objects: one being responsible for set-oriented operations and collections and the other one for individually oriented operations. If, on the other hand, your best estimate of the situation is that Horse objects will always be stateless, you can go for the simpler and more manageable solution: a single class for all horse operations.

If some objects might be stateful

Figure 8-3 shows a design in which you assume that stateful Horse objects might be needed now or later.

click to view at full size.

Figure 8-3. Objects of the Horse class carry out individual horse-oriented operations.

In such a design, "plural" objects carry out plural operations and "singular" objects carry out singular operations.

Figure 8-4 highlights the difference between stateful and stateless Horse objects. It shows a set-oriented Horses class with the typical collection methods and properties: Add, Remove, Count, and Item. It also shows how each individual Horse object has the properties Name, BredInCountry, Sex, and Birthyear, allowing each Horse object to carry information about a single horse and to be responsible for the keeping of at least temporary state about that horse. Such a design is beautifully object oriented, which, however, isn't the same as being efficient in all situations. It's definitely not the same as being scalable.

Figure 8-4. In the stateful model, a Horses collection contains Horse objects, each Horse object representing a single horse.

If all Horse objects will always be stateless

Figure 8-5 shows a different situation. In this design, we've allowed a single class to be responsible for set-oriented as well as individually oriented operations.

click to view at full size.

Figure 8-5. In the stateless model, objects of the Horses class carry out set-oriented as well as individually oriented horse operations.

This is a stateless design. It doesn't allow the Horses class to have a collection of Horse objects, each one responsible for its own set of properties.

Not entirely stateless

A design like the one in Figure 8-5 doesn't have to be fully stateless, however. If you want, you can add a recordset to the Horses class, the content of which can represent a set of horses. As a matter of fact, such a solution is in many cases more efficient than the stateful design shown in Figure 8-4. It isn't as nicely object oriented, but then again, you must decide whether you want object orientation to be a goal or a tool. You might say that such a design is "state flexible." In fact, this is how some Microsoft folks refer to Microsoft Transaction Server (MTS) and COM+.

Anyway, for the purposes of this book, we've chosen to implement the stateless design version. We've done so for scalability reasons and because HTTP is a stateless protocol. We'll talk about both these reasons separately, starting with the scalability reasons.

Scalability

A server object that stays alive between calls, and also keeps state for its client, always consumes resources. A client can easily keep hundreds of objects alive on a server. This means that thousands of clients can keep hundreds of thousands of objects alive. Every one of these objects consumes memory space. They also consume other kinds of resources.

Real or virtual memory, network connections, and database connections are all examples of scarce resources. Servers shouldn't allow clients to take up more resources than each client needs at any particular moment.

Threads, or rather processors that run threads, are scarce resources too. Both COM+ and MTS do a very good job of managing threads for you as efficiently as possible. Processes in COM+ and MTS keep thread pools for their clients, thereby serving clients with threads on which to run their objects. Each process in MTS holds such a thread pool with a default but configurable maximum of 100 threads. Each COM+ process holds a similar thread pool, the size of which COM+ calculates, using the number of processors to arrive at the correct pool size. The goal is to allow each processor to spend most of its time performing instructions on threads and less time creating, managing, and switching between threads.

What we say about COM+ and thread pools is in general equally true for MTS and thread pools. However, in COM+ you have the option of pooling objects as well as threads.

At present, components developed in Visual Basic 6.0 can't take advantage of object pooling. The only threading model you can use in Visual Basic is the apartment model, in which objects have thread affinity. Once created on a thread, an object with thread affinity never accepts running on any other thread. With object pooling, objects must be able to be activated on any thread, which disqualifies Visual Basic components from object pooling. An uninformed guess is that this situation will change with the next generation of Visual Basic.

NOTE
In the paragraphs that follow, we're talking specifically about COM+ applications, although in MTS a corresponding concept is represented by the MTS package. We're also talking strictly about components that are created by means of Visual Basic 6.0 (or Visual Basic 5.0, for that matter).

When the first client asks COM+ for an object that's installed in a COM+ application, COM+ starts the application's process, creates a thread, and allocates the object to that thread. For the next client, COM+ creates a new thread and so on up to the maximum number of threads in the pool.

When objects are deactivated, COM+ recycles their threads to the thread pool. When a client needs a thread for a new object, COM+ looks in its thread pool. If a thread is available in the pool, COM+ uses it. If not, COM+ creates a new one, as long as the maximum number of threads allowed aren't already created.

If the maximum number of threads has been reached, COM+ allocates the new object to a thread that's already busy running other objects for other clients. When this happens, performance decreases and clients have to wait for one another to have their tasks completed. COM+ (and Microsoft Windows 2000) have to switch context between objects that share the same thread, which also has a bad effect on performance. The more clients and the more live objects, the worse the performance. Obviously, this is a situation you should avoid.

If you make your objects follow the just-in-time (JIT) activation and as-soon-as-possible (ASAP) deactivation scheme, there's a good chance that COM+ will find a thread in the thread pool for each client request. Then no object will ever exist when it's not needed. The fast in_fast out strategy allows COM+ to efficiently use and reuse the available threads, even if there are several thousand simultaneous clients. The think time each client normally needs is enough to make such a system, based on stateless server objects, incredibly scalable.

HTTP is a stateless protocol

Our second reason for choosing the stateless design version was our claim that HTTP is a stateless protocol. Let's see why we say that, and why we think this is a reason to go for a stateless design.

In principle, when a request from a Web client comes to a Web server, the client uses the HTTP protocol to send that request. As long as the Web server is in the process of responding to the request, the server knows the address of the client. It must, because otherwise it wouldn't know where to send the response. But as soon as the server has responded, it (in principle) forgets the address of the client. And when the client receives the response, the client's connection to the server is also released. If the client wants to send another request to the same server, it must set up another HTTP session with it. This is why HTTP is said to be a stateless protocol.

This also means that the client can't keep any connections to business objects that reside in the server between requests to that server. In fact, with HTTP there's no guarantee that the next request goes to the same server. If the server is part of a Web farm, which is a collection of equal Web servers all performing the same set of services, the next request might very well go to another server in the same Web farm.

You could compare that with DCOM. When a client wants to communicate with a server using DCOM, it sets up a connection to the server. This connection will stay up as long as the client wants it to. DCOM sessions lasting a full day are not unheard of. The client can go back again and again to the same server object, asking for additional services. That's why DCOM is said to be a stateful protocol.

If you use DCOM, it might make sense to use stateful server objects. You can keep track of them, so you can rely on them to keep state for their clients—a given state is always available to a given client. Using HTTP, you can't keep track of any server objects, so if you plan to use HTTP a stateful design doesn't make much sense. We firmly believe that you'll use HTTP more and more for transport of pages and information in the near and not-so-near future. Parts of the application we create for this book will definitely be accessed over the Web, so HTTP will absolutely be used with it.

In this new world of the Web, the statelessness of HTTP is one of at least two very good reasons to opt for stateless objects as much as possible.

Delegating Other Facade Operations

So far, we've delegated two operations of the facade class to the Horses class. The operations we haven't delegated yet can be divided into two categories: those that should be delegated to the Horses class and those that should be delegated to other classes, not yet existent in our model. Figure 8-6 shows how all the remaining horse-related operations in the facade class are delegated to the Horses class.

click to view at full size.

Figure 8-6. Other horse-related operations, required by the facade object, are now added to the Horses class.

Let's take a look at Figure 8-7. It shows how the GetTrainerList method in the facade class delegates its job to a corresponding method in the new Trainers class.

click to view at full size.

Figure 8-7. Objects of the Trainers class fetch lists of horses. Note the operation name in the Trainers class, which leaves the class open for other list-getting operations as well.

You might find the name of the method in the Trainers class—GetListForNamePattern—a little unusual. Why, you might ask, have these guys chosen a name different from the one in the facade class?

Here's the reason. This facade class needs a list of trainer names, and only one type of selection criterion, if any, will be of interest. Users of this facade won't be interested in selecting a smaller set of trainers by any means other than a pattern with which to match the trainer's last name. For example, a pattern like Jo fetches trainer Johansson and all the other trainers whose last names begin with the letters "Jo."

So, from the perspective of this particular facade, the issue is a very simple one. From the perspective of the Trainers class, things might be different. Other facades might be interested in selecting amateur trainers only, or professionals only, or perhaps amateurs and professionals both; yet others might exclusively want trainers who have trained winners of races run over 1600 meters on a grass track. Such facades might represent future needs, unknown now, or even present needs coming from other use cases and thus other facades.

In other words, a class such as the Trainers class might very well need more than one list-fetching operation.

Because the Trainers class needs to separate different list operations from one another, the class is better off using operation names such as GetListForNamePattern. For the facade, on the other hand, it's perfectly satisfactory to use a name such as GetTrainerList. Unlike main business objects, such as those of the Trainers class, facades can ignore everything but the requirements of the one—and often only—use case they support.

NOTE
Establishing specific naming patterns appropriate to their class and use is extremely important, at least in our opinion. In our view, facade objects belong to a certain application. Main business objects, such as Horse or Horses, Trainer or Trainers, belong to the entire business; they're made for reuse over several applications.

Figure 8-8 completes the picture by adding the Countries class with the GetAll method. In this particular case, given the fairly small number of countries breeding racehorses in the world, a GetAll method is quite acceptable. Such a list would never contain more than a couple hundred elements.

click to view at full size.

Figure 8-8. A set of entity classes now supports the entire fcdHorseMaint facade class.

The benefit of a facade class

Gradually the usefulness of facade classes becomes apparent. They make things simpler for UI objects and especially for UI developers because they allow developers to work with a single class rather than, as in this example, three.

You'll soon see how this simplification means even more when we've added separate COM interfaces to the three entity classes. You might also remember that this facade and this user application are extremely simple. The more complicated the application, the more useful the facade 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