Classes are the most important building block of any object-oriented
system. A class is a description of a set of objects that share the same
attributes, operations, relationships, and semantics. A class implements one or
more interfaces.
You use classes to capture the vocabulary of the system you are
developing. These classes may include abstractions that are part of the problem
domain, as well as classes that make up an implementation.
Modeling a system involves identifying the things that are important to
your particular view. These things form the vocabulary of the system you are
modeling. In the UML, all of these things are modeled as classes. A class is an
abstraction of the things that are a part of your vocabulary. A class is not an
individual object, but rather represents a whole set of objects.
The UML provides a graphical representation of class, as well, as the
below figure shows. This notation permits you to visualize an abstraction apart
from any specific programming language and in a way that lets you emphasize the
most important parts of an abstraction: its name, attributes, and operations.
Names
Every class must have a name that distinguishes it from other classes.
A name is a textual string. That name alone is known as a simple name; a path
name is the class name prefixed by the name of the package in which that class
lives. A class may be drawn showing only its name, as shown below:
A class name may be text consisting of any number of letters, numbers,
and certain punctuation marks (except for marks such as the colon, which is
used to separate a class name and the name of its enclosing package) and may
continue over several lines. In practice, class names are short nouns or noun
phrases drawn from the vocabulary of the system you are modeling. Typically,
you capitalize the first letter of every word in a class name, as in Customer or
TemperatureSensor.
Attributes
An attribute is a named
property of a class that describes a range of values that instances of the property
may hold. A class may have any number of attributes or no attributes at all. An
attribute represents some property of the thing you are modeling that is shared
by all objects of that class. At a given moment, an object of a class will have
specific values for every one of its class's attributes. Graphically,
attributes are listed in a compartment just below the class name. Attributes
may be drawn showing only their names, as shown below:
In practice, an attribute name
is a short noun or noun phrase that represents some property of its enclosing
class. Typically, you capitalize the first letter of every word in an attribute
name except the first letter, as in name or loadBearing.
You can further specify an attribute by stating its class and possibly
a default initial value, as shown below:
Operations
An operation is the
implementation of a service that can be requested from any object of the class to
affect behavior. In other words, an operation is an abstraction of something
you can do to an object and that is shared by all objects of that class. A
class may have any number of operations or no operations at all. Graphically,
operations are listed in a compartment just below the class attributes. Operations
may be drawn showing only their names, as shown below:
In practice, an operation name
is a short verb or verb phrase that represents some behavior of its enclosing
class. Typically, you capitalize the first letter of every word in an operation
name except the first letter, as in move or isEmpty.
You can specify an operation by stating its signature, covering the
name, type, and default value of all parameters and (in the case of functions)
a return type, as shown below:
Organizing Attributes and Operations
When drawing a class, you
don't have to show every attribute and every operation at once. An empty compartment
doesn't necessarily mean there are no attributes or operations, just that you
didn't choose to show them. You can explicitly specify that there are more
attributes or properties than shown by ending each list with an ellipsis ("...").
To better organize long lists of attributes and operations, you can also prefix
each group with a descriptive category by using stereotypes.
Responsibilities
A responsibility is a contract or an obligation of a class. When you
create a class, you are making a statement that all objects of that class have
the same kind of state and the same kind of behavior. At a more abstract level,
these corresponding attributes and operations are just the features by which
the class's responsibilities are carried out. A Wall class is responsible for knowing
about height, width, and thickness.
When you model classes, a good starting point is to specify the
responsibilities of the things in your vocabulary. Techniques like CRC cards
and use case-based analysis are especially helpful here. A class may have any
number of responsibilities, although, in practice, every well-structured class
has at least one responsibility and at most just a handful.
Graphically, responsibilities can be drawn in a separate compartment at
the bottom of the class icon, as shown below:
Responsibilities are just free-form text. In practice, a single
responsibility is written as a phrase, a sentence, or (at most) a short
paragraph.
Common Modeling Techniques
Modeling the Vocabulary of a System
To model the vocabulary of a system,
- Identify those things that users or implementers use to describe the problem or solution. Use CRC cards and use case-based analysis to help find these abstractions.
- For each abstraction, identify a set of responsibilities. Make sure that each class is crisply defined and that there is a good balance of responsibilities among all your classes.
- Provide the attributes and operations that are needed to carry out these responsibilities for each class.
Below figure shows a set of classes drawn from a retail system,
including Customer, Order, and Product. This figure includes a few other
related abstractions drawn from the vocabulary of the problem, such as Shipment
(used to track orders), Invoice (used to bill orders), and Warehouse (where
products are located prior to shipment). There is also one solution-related abstraction,
Transaction, which applies to orders and shipments.
Modeling the Distribution of Responsibilities in a System
To model the distribution of responsibilities in a system,
- Identify a set of classes that work together closely to carry out some behavior.
- Identify a set of responsibilities for each of these classes.
- Look at this set of classes as a whole, split classes that have too many responsibilities into smaller abstractions, collapse tiny classes that have trivial responsibilities into larger ones, and reallocate responsibilities so that each abstraction reasonably stands on its own.
- Consider the ways in which those classes collaborate with one another, and redistribute their responsibilities accordingly so that no class within a collaboration does too much or too little.
For example, below figure shows
a set of classes drawn from Smalltalk, showing the distribution of
responsibilities among Model, View, and Controller classes. Notice how all
these classes work together such that no one class does too much or too little.
Modeling Nonsoftware Things
To model non-software things,
- Model the thing you are abstracting as a class.
- If you want to distinguish these things from the UML's defined building blocks, create a new building block by using stereotypes to specify these new semantics and to give a distinctive visual cue.
- If the thing you are modeling is some kind of hardware that itself contains software, consider modeling it as a kind of node, as well, so that you can further expand on its structure.
As below figure shows, it's
perfectly normal to abstract humans (like AccountsReceivableAgent) and hardware
(like Robot) as classes, because each represents a set of objects with a common
structure and a common behavior.
Modeling Primitive Types
To model primitive types,
- Model the thing you are abstracting as a type or an enumeration, which is rendered using class notation with the appropriate stereotype.
- If you need to specify the range of values associated with this type, use constraints.
As below figure shows, these things can be modeled in the UML as types
or enumerations, which are rendered just like classes but are explicitly marked
via stereotypes. Things like integers (represented by the class Int) are
modeled as types, and you can explicitly indicate the range of values these
things can take on by using a constraint. Similarly, enumeration types, such as
Boolean and Status, can be modeled as enumerations, with their individual
values provided as attributes.