Modeling User Services and Facades

[Previous] [Next]

Now let's move a little closer to reality. We need to find out more about what the system will have to do for us, and about which classes should do the job at hand. We already have our framework; we even know some of the classes needed. Let's go to work on it.

A Class Diagram

Our first job is to create a small diagram that illustrates at least some of the classes that will be involved in our little sample application. We need at least a graphical user interface (GUI) object; we'll use for the time being a Visual Basic form. We also need a facade class, which will be the only place the form needs to go to for business services. According to our design pattern, we also need recordsets to keep our data and to move it between objects. Figure 5-2 shows the relationship of these objects to one another.

Figure 5-2. The frmHorseMaint form object relies entirely on the fcdHorseMaint facade object for business services. Both of them use an ADO recordset for state keeping and data transformation.

A use case is a high-level statement of something an actor needs to do with the system. Every use case specification needs to list things a user needs to do to carry out the use case. One of the use cases our application needs to support concerns maintaining information on horses trained to run in horse races. Let's list a few of the things a user needs to do in that use case. Then we can see the kind of services required for each of these things. Here's such a list:

  • The user needs to modify or delete data about already registered horses. This would involve
    • Getting a selected set of horses from the database
    • Selecting one of those horses
    • Presenting horse data in several form fields
    • Editing the horse data
    • Saving the modifications to the database
    • Deleting or deactivating a horse in the database

  • The user needs to enter new horses into the system. This would involve
    • Getting a new empty record in an ADO recordset
    • Entering information about the new horse in several form fields
    • Saving the information to the database

Let's see how each of these requirements could be designed and implemented in a first prototype of the application. In the following several chapters, we'll refine that prototype until it becomes the real application.

Getting a Selected Set of Horses from the Database

Let's begin to explore how we might accomplish this. Figure 5-3 shows a first version of our Visual Basic form in its proper environment—in Visual Basic's integrated development environment (IDE).2

Figure 5-3. A text box and an OLE DB DataList control will help the user find the right horse.

The Search Criteria text box allows the user to type a name pattern for the horses that should populate the OLE DB DataList control beneath it. This name pattern must travel all the way to the database to be used as a selection criterion. We need a mechanism for that, and also one for displaying the horse names coming from the database. The diagram in Figure 5-4 shows the methods that form such a mechanism.

click to view at full size.

Figure 5-4. This is an enhancement of the diagram in Figure 5-2. It reflects the mechanisms needed to fetch and display a list of horses.

Notice a few interesting details in Figure 5-4:

  • The form employs a method named DisplayHorseList. This method doesn't have any arguments, and it doesn't return any values. All it does is collect a list of horse names and display them in a list.
  • The DisplayHorseList method is private to the form. It must be called from within it. (The padlock to the left of the method's name indicates the private access privilege of the method.)
  • The DisplayHorseList method uses the GetHorseList method in the fcdHorseMaint facade class. This method takes a name pattern string as an argument, and it returns a recordset.
  • The frmHorseMaint form privately owns the rsHorses recordset, being one instance of an ADOR recordset. Please note the minus sign to the left of the rsHorses Unified Modeling Language (UML) role name. The minus sign indicates that the recordset is private to the form. Please also note that a solid association line has replaced the dashed dependency line in Figure 5-2.
  • A UML association translates to a Visual Basic module variable. This means in principle that the life cycle of the recordset will be the same as that of the form. A UML dependency, in contrast, doesn't translate to anything at all. It exists to indicate a dependency, such as the fact that one object has to call another one for service.

  • The facade class doesn't own the recordset object; it merely uses it and thus depends on it. The diagram represents this dependency relationship by means of a dashed line between the two classes. This relationship indicates that the form object needs to call a method in the object upon which it depends. If you wanted to, you could give the dependency relationship a name that clearly indicates the method the form needs. Figure 5-5 shows a named dependency, GetHorseList, between the frmHorseMaint form class and the fcdHorseMaint class module.
  • click to view at full size.

    Figure 5-5. In this diagram, a dependency relationship has been given a name corresponding to the method that needs to be called.

The facade delegates its tasks

Just a few words about the facade object. The facade object itself won't go to the database for the information requested. Instead, it delegates that work to an object of the Horses class, which in turn delegates it to a HorseFetcher object. Right now we don't care. All we want to do at this particular moment is define exactly which services the user application needs from the server application, how it should request these services, and how it should get access to the results of the services. We've already defined these aims for the very first action from the user, which is to ask for a specific list of horse names. The facade object exposes this service with the GetHorseList method.

Before doing anything else, we should specify exactly what this method does. Since we don't know at this point how it will perform its service, we'll only be able to describe what the service is, what it will be called, and what it will return. Figure 5-6 gives an example of such a specification.

Figure 5-6. The GetHorseList method specification. Later on we'll enhance it to specify which services the method will use to do its job.

Later on, when we use the Rose model to generate Visual Basic code, the code generator will include this specification as a method comment.

Now let's see how the facade class exposes the next mechanism that we need to use.

Selecting One of the Horses Displayed

This is a tricky task, much trickier than it would seem to be on the surface. If the goal were to create an ordinary two-tier application, the recordset already fetched would probably contain the information needed to display all the data about the selected horse. Furthermore, the recordset would still be in touch with the database, so every change to a record would go directly to the database as soon as the user made another record the current one.

Not so in this case. Here we're talking about a three-tier application, and the recordset isn't connected to the database at this point. Furthermore, the recordset is small in that it contains only the names and IDs of the horses selected. The recordset contains names because they must appear in the data list control, and it contains IDs because we need them to get full information about a selected horse.

So we need to go to the database again, this time to select data on a single horse, identified by its ID. Figure 5-7 shows how we've enlarged the model to accommodate this requirement.

click to view at full size.

Figure 5-7. The model now reflects the capability to get and display a selected horse from the database.

The changes in the diagram speak more or less for themselves, but here are a few comments just the same:

  • The form's DisplayHorse method is private to the form, just as DisplayHorseList is. It won't take any arguments, and it won't return anything. All it will do is request from the server application a list of names and IDs of horses. Once it gets that information, it will display the horse names received on the form. After doing that, its job is done.
  • The DisplayHorse method makes use of the facade object's GetHorseById method. This method takes a HorseId value, its data type being long, as its only argument. It returns a recordset that should contain data on the horse selected. Figure 5-8 shows how you might create the specification for such a method.
  • The form saves this recordset, with its content, in a module-level recordset variable, rsHorse. You can see this in the diagram in Figure 5-7. The association relationship of the form to the ADOR recordset represents such a variable. So the form now permanently keeps data in two different recordsets. You can see that in the diagram, since there are now two different associations between the form and the recordset.
  • The first of these associations, the one named rsHorses, contains names and IDs of all the horses displayed in the list. The second one, named rsHorse, contains full data on the horse selected and helps display that data in the main part of the form (as in Figure 5-9).

    Figure 5-8. The GetHorseById method specification.

Presenting Data for a Horse in Several Form Fields

The form in Visual Basic's IDE now needs a number of fields so that it can display detail data for a single horse. Let's add some simple text boxes to our form, as in Figure 5-9. Later we can enhance the form by exchanging a couple of the text boxes with other controls better suited to our purpose.

Figure 5-9. The form now contains fields to display data that describes a single horse.

Changing the Contents of Form Fields

Next on our list of user requirements is the need to change the content of some of the fields that display data for the horse. Because we're in the process of designing a facade class, we don't need to even think about how users should interact with the form. That's an issue for client developers. In the test form we're designing now, we'll use data binding between the rsHorse recordset and the form fields. In the next chapter, you'll see how this works out.

Saving Modifications

In any case, you must make sure that the user can send modified information to the database. In the form, a simple Save button is enough, and you can see it in Figure 5-10.

Figure 5-10. The form now has a Save button.

Furthermore, we'll need a save method both in the form and in the facade class. In the form, such a method will be simple enough. The method will know by itself which recordset contains the changes. It will also know how to delegate the job to the facade object, which in turn will delegate it to other objects in order to move the new information right into the database.

The facade method must be treated differently. It needs to be told about the recordset, and also about the content of the recordset, when called. Take a look at the new SaveRecord and SaveHorse methods in Figure 5-11. Notice that the SaveHorse method takes a recordset as its only argument.

click to view at full size.

Figure 5-11. The model can now save a Horse record to the database.

In Figure 5-12, you can see the specification for the SaveHorse method in the facade class.

Figure 5-12. The SaveHorse method specification.

Why doesn't the SaveHorse method return a status code?

Looking back at the class diagram in Figure 5-11, you might ask yourself why the SaveHorse method doesn't return a status code. After all, such a return code would tell the form object whether or not the save operation was successful. Shouldn't the form object be told?

Yes, it should, and it will; at least if there is an error. As we see it, you can choose from two ways to handle errors:

  • The C way (which admittedly isn't the only way in C), which requires every called function to return a result code (or, if you prefer, an error code). It also requires every client to ask for and act on that result code. So an If…Then statement will surround every method call.
  • The Basic way, in which the caller assumes that the call will succeed. The called subroutine or function raises an error if anything goes wrong. The error will bubble up all the way to the client, which will treat it as an exception and act on it. A method call won't have to be surrounded by an If...Then statement or anything else for that matter. The code will be much more straightforward, easier to create, easier to read, and easier to maintain.

You should use only one of these two ways of handling errors and stick with it. Don't fall into the trap of using one of them sometimes, the other one on other occasions. Pick one only!

Our pick is the second one—the Basic way. We'll use it for the rest of this book, and we recommend it to you as well.

Deleting a Horse

In a way, deleting a horse follows the same pattern as saving modifications. When you design a method for deleting horses, you must consider rule 6, which we introduced in Chapter 3 and whose implementation we described in Chapter 4: horses that have participated in races must not be deleted but merely deactivated.

This rule, however, doesn't affect our work at this point. We still need a way for the user to order the deletion. In the facade class, we also need a mechanism for sending that command to the inner parts of the server application. In Figure 5-13, you can see how we use the horse's ID and the DeleteHorse method to delete the horse's record rather than send the recordset to the facade.

click to view at full size.

Figure 5-13. Delete operations are now included in the model.

You could, if you prefer, include the recordset with the delete message instead of including the ID.

You could even use the Save method to delete a horse from the database. If you mark the record for deletion and then save it, a SQL DELETE statement will be sent to the database server. Each record has a state property, informing ADO of the type of modification—if any—that the record has been subjected to.

It's all a matter of taste, really, which of these three means to use. Whichever one you choose, however, you must take into account the fact that a requested deletion might be changed to an update, deactivating the horse. When this happens, the user must be told about it. Assuming we've chosen the first of the three means (seen in the diagram in Figure 5-13), let's see how we make sure the user is informed.

In the documentation of the facade class, we inform anyone who might read the code about the existence of this rule and, most important, about how the method should react when a deletion is changed to a deactivation. You can see this in Figure 5-14.

Figure 5-14. The DeleteHorse method specification.

We don't document the specifics of the rule in the facade class. This is because it's not the responsibility of the facade object to check for the rule. It is, however, important for the creator of the facade class to know that there is such a rule and that facade objects of the class might be affected by it. This is what we try to describe in the facade class documentation.

Getting a New Empty Record to Add a New Horse

When a user wants to add a horse to the database, he or she needs an empty record in the rsHorse recordset. Right now we don't have to consider how to give the user such a record, but we must specify the interface to the mechanisms that we need. This is easy enough, as you can see in Figure 5-15.

click to view at full size.

Figure 5-15. The DisplayEmptyRecord and GetEmptyRecordset methods for setting up empty recordsets are added to the diagram.

You can see that two new methods have been added:

  • DisplayEmptyRecord, in the form. It displays an empty record in the form fields. It gets the empty recordset by calling the facade's GetEmptyRecordset method.
  • GetEmptyRecordset, in the facade object. Incidentally, we could think of dozens of other names for this method, none of which we really like. This is the one we dislike the least.

Let's just take a look at the specification of the facade object's GetEmptyRecordset method. See Figure 5-16.

Figure 5-16. The GetEmptyRecordset method specification.

All the methods we've introduced in this chapter will be implemented in several classes spread over all five tiers of our basic architecture. The next eight chapters are dedicated to the design and implementation of these classes. However, in Chapter 6, "Reducing Time to Market," we describe a way to reduce time to market by starting a number of activities at this early stage rather than much later in the project.



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