Introduction to UML Class Diagrams
The Unified Modeling Language (UML) is a standard for systems modeling. It describes both the structure and the behavior of a system. This article explains how to model the structural aspects of a system using class diagrams.The Unified Modeling Language (UML) is a semi-formal standard for systems modeling. It is rigorous enough to be executable and flexible enough to capture a system's various aspects.
UML provides a model of a system's structure and collaborative behavior, as well as a detailed specification of the behavior of system objects and components. In this article, we'll focus on modeling the structural aspects of a system using the class diagram. In two subsequent installments, we will discuss using sequence diagrams and statecharts to address behavior.
A system's structural model consists of a set of class diagrams. Some of these diagrams will show the subsystem architecture, where the primary elements are big objects, such as subsystems and components; others may concentrate on the tasking structure and focus primarily on the active objects (but may also show things such as semaphores and event queues); other diagrams may show the collaboration structure among classes-that is, how objects work together to realize system-level behaviors; still other diagrams may show the organization of the model, primarily depicting packages and their relations. These are all different aspects of the structure of the same system.
A class is a specification of the low-level structure and behavior of some set of objects. Classes specify an object's attributes (data elements) and methods (member functions). The class may also specify via a statechart or activity diagram how instances will respond to incoming events.
If a class is a "cookie cutter" then an object is a "cookie." An object is an instance of a class, much as 3.14159 is an instance of a floating-point numeric type. All objects instantiated from a particular class have identical structure and behavior, though the values of each object's attributes may (and usually do) differ.
When you want to capture some structural aspect of a system, you usually start with its universal characteristics (design time concepts related to the pieces of the system and how they relate to each other), rather than the structure of the run-time system at some particular snapshot in time. So you'll usually capture the structure with classes and their relationships, understanding that, at run-time, instances of these classes may or may not exist at different points in time. When you want to capture a snapshot of some instant in time, you use objects.
Figure 1: A UML class diagram
Figure 1 shows a typical class diagram. The boxes are classes of various kinds. The Power_Subsystem class represents a subsystem-a large architectural object that "contains" smaller objects that provide the subsystem's functionality. The PowerClient and Knob classes are shown in "canonical form" while the Light class shows some set of its attributes and methods in addition to its name. The attributes are typed-color has the enumerated type COLOR_TYPE, and intensity has the type int. In the bottom segment of the Light class, we see methods that take parameters and possibly return values. The DisplayThread class is a special active class, which means that instances of this class run in their own threads of execution. The DisplayThread class owns an instance of the Light class, which runs in the thread of the owning DisplayThread object.
Objects are merely pieces of the system, so they must collaborate with other objects to provide systemwide behavior. This requires links (often C or C++ pointers) between the objects to invoke services or send events to each other. On the class diagram, this is shown as an association between classes. In Figure 1, three different kinds of associations are shown.
The simple lines are normal associations. Such associations between classes mean that at run-time, there may be active links between the instantiated objects, allowing services to be invoked between objects. An open arrowhead, where present, indicates that messages flow only in one direction.
A clear diamond on the line (for example, between the SolarPanel and PositionSensor classes) indicates an aggregation, a whole-part form of association. A strong form of aggregation called composition is shown either with a filled-in diamond (as between Power_Subsystem and SolarPanel) or by nesting one class inside of its owner (as between Power_Subsystem and Battery). Composition is a whole-part association but also means that the composite owner is responsible for both the creation and destruction of the part object.
With any type of association, you may also indicate multiplicity, or the number of instances of the class that participate in the role at run-time. For example, you can see that there is a single (1) Knob object that associates with each PowerClient. On the other hand, there are zero or more (*) SolarPanels for each Power_Subsystem. For the nested notation, the part multiplicity is shown in the upper corner of the part class. For example, we can see that the Power_Subsystem owns two Battery objects.
UML defines other relationships among classes besides association. The most common one is generalization-an "is-a" relation such as that between PowerClient (the more general form) and Light (the more specific form). (A Light "is-a" PowerClient.)
The semantics of generalization ensure that things that are true about PowerClient (its attributes, operations, and associations) are also true about the Light class. The Light class can specialize the operations defined in the PowerClient class and can also define additional attributes and operations beyond those provided by the PowerClient. This means, for example, that the Light class actually has an association with a Knob class, because its parent class has such an association.
The last thing we see in Figure 1 is a constraint, which is a "rule of correctness" for adding additional semantics about the system that are not defined through the use of classes and relationships. In this case, we wanted to specify that the active power source can be either the battery or the solar panels but not both. Constraints are used to capture the nonfunctional (commonly known as "quality of service") aspects of the system, such as worst or average-case performance of methods, capacity, and so on.
A touch of class
UML is a powerful language for capturing the different aspects of a system. Its class diagram is a flexible graphical notation that can show the system structure at a glance and at different levels of abstraction. These aspects are predominantly the structural elements of the system and how they relate to each other both in terms of how collaboration is supported and in terms of the numbers of instances involved. The addition of constraints enables the developer to easily capture specialized concerns and limitations of the structure as well.
Bruce Powel Douglass is the chief evangelist at I-Logix. He has over 25 years' experience designing safety-critical real-time applications and is the author of several books including Real-Time UML, Doing Hard Time, and Real-Time Design Patterns. He can be reached at firstname.lastname@example.org.