|
|
Modeling Time in UML Class Diagrams The Unified Modeling Language (UML, see http://www.omg.org) defines two basic categories of representation: structural and behavioral diagrams. Behavioral representations are presented as use case diagrams, interaction diagrams, state machine diagrams, and activity diagrams. Behavioral representations directly incorporate time-dependent and control aspects of the system being modeled. Structural representations include class diagrams, object diagrams, component diagrams, and deployment diagrams. Structural representations show static structure without incorporating a time element. Class diagrams, in particular, represent a snapshot of the structural relationships among the classes of the system being modeled, and one cannot trace a flow of events and actions using a class diagram. But it does not take a new OO modeler long to run into a fundamental issue in class diagrams: time does make a difference in how the diagram is built. And experienced modelers wrestle with this issue as well. Let's look at a simple example which will make clear how time must be considered in order to construct a reliable and useful class diagram. Consider an application which manages the relationship between a hotel guest and the hotel room occupied by that guest. In a UML class diagram this relationship is represented as an association between one class, Guest, and another class, Room. The following UML class diagram, Fig. 1, specifies that an instance of the class Guest resides in an instance of the class Room. Multiplicity specifiers have purposely been omitted from this diagram because the question "How many Rooms is a Guest associated with, and how many Guests is a Room associated with?" is exactly the issue we need to address.
Fig. 1. The first option is to associate one instance of Room with each instance of Guest (Fig.2). This diagram explicitly annotates each end of the association with a multiplicity of one. Note: Although UML makes provision for a default multiplicity of one if the specifier is not included on an association, it is always a better idea to explicitly note every multiplicity to prevent mis-interpretation, and to make clear that a multiplicity was not accidentally omitted.
Fig. 2. In Fig. 2 an instance of Guest knows about only one instance of Room at a time, and only one Room over time. This instance of Room knows about only one Guest at a time, and only one Guest over time. Clearly, this model gives us a snapshot of this association only at one point in time. This model can answer questions which would be appropriate to a snapshot: for example, "What Room is a specific Guest residing in now?" and "What Guest is in a specific Room now?” This one-to-one association models a very small part of a simple reservation system which can only maintain information about the immediate present. If your problem domain requires only a simple snapshot of this relationship, then Fig. 2 will satisfy that need. But we often need to capture and manage more knowledge in our applications. In Fig. 3, below, the multiplicity on class Room has been promoted to one-or-more (1..*) so that our system can capture all of the rooms that a guest occupies during a stay at a hotel, or all of the rooms occupied in (say) a one-year period.
Fig. 3. Unlike Fig. 2 the model of Fig. 3 handles not only the immediate present, but also an entire history of the rooms a guest has occupied. An instance of Guest still resides in only one instance of Room at a given time, but each Guest knows about one or more Rooms occupied over time. Each instance of Room, however, still knows about only one Guest at a time, and only one Guest over time. In this model we can answer questions such as: “What room is this guest occupying now?” “What rooms has this guest been in over time?”, or “What guest is in this room now?” This simple change in the model to capture time-history is a very useful change if the business model supports the idea of "Frequent Guest" awards which accrue over time. It is also a very useful change for a much more practical reason. Imagine (as often happens to me on my travels) that you check into a room in Dallas, Texas in August, you make a long-distance telephone call charged to the room, discover the air-conditioning does not work, and the hotel moves you to another room. If the snapshot model of Fig. 2 is used, the telephone charge in the original room might be lost. Keeping a history of rooms within an uninterrupted guest "stay" allows all charges to be maintained on a room basis, and nothing will be lost by moving a guest to another room. As an interesting—and very real-world scenario that I worked on—consider a hotel which discovers that one of its guests has died of a virulent and infectious disease. In the simple model of Fig. 2, the hotel could only determine the room currently occupied by the deceased, even if the guest had actually occupied 3 rooms in the hotel stay. Fig. 3 is much more informative: it allows us to determine all 3 rooms that the deceased guest occupied so that medical authorities can quarantine those rooms. But the model of Fig. 3 does not allow us to determine what other guests have stayed in those same 3 rooms during the contamination period so they can be notified of potential infection. To satisfy this requirement we must maintain a time-history in two dimensions (Guest to many Rooms, plus Room to many Guests), and we need to make another change to our structural model. In Fig. 4, below, we promote the multiplicity on class Guest to one-or-more, now giving us a many-to-many relationship between Guest and Room. Note that I have introduced an association class, DateRange, to resolve the ambiguities inherent in this many-to-many relationship. Our model now gives us a great breadth of information. An instance of Guest still resides in only one instance of Room at a time, but that Guest instance knows about one or more Rooms over time. Each instance of Room knows about only one Guest occupying it at a time, but also knows about one or more Guests over time. We have a snapshot in time, plus a history of Rooms occupied by a given Guest, plus a history of Guests which occupied a given Room. We can now ask questions such as: “What rooms has this guest been in over time?”, or “What guests have been in this room over time?”, or “What guest is in which room now?”
Fig. 4. The issue then is not so much how to directly model time within a UML class diagram, but how does the need to maintain relationships over time directly affect the static class model? The naive answer is to make every relationship a many-to-many so that you can maintain all possible time-histories. But implementation and maintenance complexity increases rapidly with each increase in multiplicity. In the simple one-to-one model of Fig. 2, C++ pointers or Java references are easily used to capture the relationship. In the many-to-many model of Fig. 4, we must incorporate map classes (sometimes call Dictionary classes) in each direction, with the accompanying overhead of traversal, persistent storage, etc. Keep your multiplicities as simple as possible, but not simpler than the problem requires. And by keeping the need for time-histories in mind, you will produce models that are more accurate and flexible. |
|