The Class Diagram
Chapter Twenty-One
Syllabus topic Module 1, "System Modeling using UML: ... Class Diagram".
Pages 125 to 131 of 499
In one line
A class diagram shows the kinds of thing a system knows about, what each one holds, what it can do, and how they are related: the system's vocabulary, drawn as boxes and lines, true at every moment rather than describing any one event.
In the wording to use when asked: a class diagram is a UML structure diagram that shows classes with their attributes and operations, and the relationships between them: associations with their multiplicities and navigability, aggregation and composition, generalisation and dependency. It describes the static structure of a system, irrespective of time.
A class, drawn
A class is a rectangle with up to three compartments: its name, its attributes and its operations. Any compartment may be left out when it has nothing useful to say.
An attribute is written in UML's own grammar:
visibility / name : type [multiplicity] = default {modifiers}Only the name is required. - stockLeft : int = 0 is a private attribute called stockLeft, an integer, starting at zero. A slash before the name marks a derived attribute, one that can be calculated from others. Where no multiplicity is written, an attribute holds exactly one value.
An operation is written visibility name(parameters) : return type, such as + takeStock(n : int) : boolean.
Visibility has four marks:
| Mark | Visibility | Who can use it |
|---|---|---|
+ | public | anything that can see the class |
- | private | only the class itself |
# | protected | the class and its subclasses |
~ | package | anything in the same package |
An enumeration is a class of named values and nothing else, drawn with the keyword «enumeration» and its values listed, such as an order's status.
Relationships
| Relationship | What it means | How it is drawn |
|---|---|---|
| Association | objects of one class are linked to objects of another | a solid line, optionally named, with a multiplicity at each end |
| Navigability | from one end you can reach the other | an open arrowhead at the end that can be reached; a small x where it cannot |
| Aggregation | a loose whole and part; the precise meaning is left to the modeller | a hollow diamond at the whole's end |
| Composition | the part belongs to at most one whole at a time, and is deleted with it | a filled diamond at the whole's end |
| Generalisation | one class is a more specific kind of another, and inherits from it | a solid line with a hollow triangle pointing at the general class |
| Dependency | one class uses another, so a change to the second may affect the first | a dashed arrow from the user to the used |
Multiplicity, the part examiners ask about
A multiplicity at the end of an association says how many objects can be at that end: 1 exactly one, 0..1 none or one, 0.. or any number, 1..* at least one, 1..10 between one and ten.
The Class Diagram
Read it from one class, across the line, to the number at the far end. On a line between User and Order with 1 at the User end and 0..* at the Order end: a user places any number of orders, and an order is placed by exactly one user.
One rule from the specification is worth knowing: where a diagram shows no multiplicity at the end of an association, no conclusion may be drawn about it. Absent is not the same as "one", so write them all.
Aggregation or composition
The test is two questions about the part: can it exist without the whole, and can it belong to two wholes at once? If both answers are no, it is composition. The specification's own definition: a composite part is included in at most one composite at a time, and if the composite is deleted, its parts are deleted with it.
Aggregation, the hollow diamond, is weaker, and the specification deliberately leaves its exact meaning to whoever uses it. Many teams therefore avoid it, and draw either a plain association or a composition. That is not a mistake; a hollow diamond whose meaning nobody can state is.
Domain model and design model
A class diagram can describe two different things, and a student should say which one theirs is.
A domain model describes the things of the problem itself: orders, menu items, users. It shows their attributes, their associations, and at most the few operations that carry the important rules. It says nothing about files, frameworks or the language.
A design model describes the classes or modules of the code: every operation with its parameters, visibility, the types of the language, often classes that exist only in the program, such as a controller or a data store.
MU's syllabus places the class diagram in the design phase, before building, so a student's class diagram is usually a domain model with its key operations. The worked team's is exactly that.
The worked diagram
Figure 21.1 The worked team's class diagram, drawn by PlantUML from the source below
@startuml class
!pragma layout smetana
top to bottom direction
hide empty members
hide circle
class User {
id : int
name : string
email : string
passwordHash : string
role : Role
isActive : boolean
}
class Session {
id : string
expiresAt : datetime
}
class Order {
id : int
pickupDate : date
slot : string
status : Status
/totalPaise : int
canMoveTo(to, by) : boolean
}
class OrderItem {
quantity : int
unitPricePaise : int
}
class MenuItem {
id : int
name : string
category : Category
pricePaise : int
isVeg : boolean
isAvailable : boolean
stockLeft : int
takeStock(n) : boolean
}
enum Role <<enumeration>> {
student
staff
owner
}
enum Category <<enumeration>> {
meals
snacks
drinks
desserts
}
enum Status <<enumeration>> {
placed
preparing
ready
collected
cancelled
no_show
}
User "1" *-- "0..*" Session
User "1" -- "0..*" Order : places
Order "1" *-- "1..10" OrderItem
OrderItem "0..*" --> "1" MenuItem
Session -[hidden]down- Role
Role -[hidden]down- Category
Category -[hidden]down- Status
@endumlThe Class Diagram
Reading it
User 1 to 0..* Session, a composition. A user may be signed in on several devices at once, a phone and a laptop, each a session. Each session belongs to exactly one user and cannot outlive them, so the diamond is filled; the database carries that out by deleting a user's sessions with the user (Chapter 24).
User 1 to 0..* Order, "places". Any number of orders, each placed by one user.
Order 1 to 1..10 OrderItem, a composition. An order item cannot exist without its order and belongs to only one, so the diamond is filled. The 1..10 comes from FR-7: at most 10 items in one order, so at most 10 lines. The rest of FR-7, no more than 5 of any one item and no more than 10 items in all, is a rule about quantities that no multiplicity can express; the SRS states it, and the code checks it.
OrderItem 0..* to 1 MenuItem, navigable one way. Each order item refers to exactly one menu item, and the arrow says an order item knows its menu item while a menu item keeps no list of the orders it is in. It is not a composition: the biryani exists whether or not anyone orders it.
Three enumerations. Role, Category and Status, each with exactly the values the database allows.
No generalisation. The use case diagram made the owner a specialisation of the counter staff. The class diagram does not make Owner a subclass of User. Students, counter staff and owners hold exactly the same data, a name, an email and so on, and differ only in what they may do. A difference in permissions is a value, role, not a new class. A subclass is for a kind of thing that holds different data or behaves differently.
Two operations. canMoveTo on Order and takeStock on MenuItem carry the two rules the whole system depends on: which status may follow which (FR-15), and never selling food that is not there (FR-9). Everything else is data.
One derived attribute. /totalPaise can be calculated from the order's items. The team still stores it, and stores each item's unit price in OrderItem although MenuItem has a price too, for the same reason: a price changed tomorrow must not change the total of an order placed today.
The Class Diagram
The source, line by line
class User { ... }declares a class and lists its attributes; a line with brackets, such ascanMoveTo(to, by) : boolean, is an operation.enum Role <<enumeration>> { ... }declares an enumeration and shows UML's keyword above its name.hide empty membersleaves out a compartment with nothing in it, as the specification allows, andhide circleremoves the small lettered circle PlantUML otherwise puts in every box, which is PlantUML's own decoration and not UML.User "1" -- "0..*" Order : placesis an association, with each multiplicity in quotes beside its class and the association's name after the colon.Order "1" -- "1..10" OrderItemis a composition: thedraws the filled diamond at the Order end.User "1" -- "0.." Sessionis another.OrderItem "0..*" --> "1" MenuItemis an association navigable towards MenuItem.- The
-[hidden]-lines draw nothing. They only ask the layout to stack the three enumerations in a column, which keeps the diagram narrow enough for a phone.
What a class diagram means for a JavaScript backend
The worked application has no JavaScript class called Order. Its backend reads rows from MySQL and hands them on as plain objects, and it keeps the rules in modules of functions. That is normal for a Node.js application, and it does not make the class diagram wrong: the diagram describes what the system knows and what it can do, and the code shows where each piece went when the team built it (Chapters 42 to 45).
An order, as the application sees it, has exactly the fields the store selects, each translated from its column name:
const ORDER_COLUMNS = `o.id, o.user_id AS userId,
u.name AS studentName, o.pickup_date AS pickupDate,
TIME_FORMAT(o.pickup_slot, '%H:%i') AS slot, o.status,
o.total_paise AS totalPaise, o.created_at AS createdAt,
o.updated_at AS updatedAt`;Every translation follows the camelCase rule, except one: the column pickup_slot is read as slot. That is the one name that breaks the rule, and Chapter 19's advice applies to it: it is written down, and it is translated in this one place and nowhere else.
The two operations are two functions. Order.canMoveTo(to, by) is canMove(from, to, role), where from is the order's current status:
// Who may move an order from one status to another. The
// owner can do anything the counter staff can.
function canMove(from, to, role) {
const mover = MOVES[from] && MOVES[from][to];
if (!mover) return false;
if (mover === 'student') return role === 'student';
return role === 'staff' || role === 'owner';
}The Class Diagram
MenuItem.takeStock(n) is the store's takeStock, a single SQL statement that takes the stock only if enough is left:
// Takes `quantity` from the stock only if that many are
// left, in ONE statement, so two orders for the last
// plate cannot both succeed. True if it was taken.
async takeStock(conn, id, quantity) {
const [result] = await conn.execute(
`UPDATE menu_items
SET stock_left = stock_left - ?
WHERE id = ? AND is_available = TRUE
AND stock_left >= ?`,
[quantity, id, quantity]);
return result.affectedRows === 1;
},And the derived total is calculated once, when the order is placed:
// lines: [{ quantity, pricePaise }]. Integers, so exact.
function orderTotal(lines) {
return lines.reduce(
(sum, line) => sum + line.quantity * line.pricePaise, 0);
}If your project is written in Java, C# or PHP with classes, the mapping is even more direct: each class in the diagram becomes a class in the code, and each operation a method.
Checking it against the other diagrams
- Against the ER diagram (Chapter 24). Every attribute of every class is a column of its table, and every association is a foreign key:
userIdin Order is the association to User, which is why the class diagram does not list it as an attribute. The one renamed column,pickup_slot, is explained above. - Against the sequence diagrams (Chapter 22). Every message sent to part of the system is an operation or a function it really has: taking the stock is
takeStock. - Against the state machine (Chapter 23). The six values of Status are the six states, and
canMoveToallows exactly its five arrows.
Do this for your project
- Underline the nouns in your requirements and use cases; the important ones are your candidate classes.
- For each class, list what it must remember: those are its attributes, each with a type.
- Add only the operations that carry a business rule.
- Draw the associations, and write a multiplicity at both ends of every one. Read each aloud across the line.
- Choose composition only where a part cannot exist without its whole; avoid the hollow diamond unless you can say what it means.
- Use an enumeration for a fixed set of values, and a subclass only for a kind of thing that holds different data.
- Check every attribute against your database design, and write down any name that breaks your mapping rule.
Mistakes that cost marks
Foreign keys as attributes. userId : int inside Order says the same thing as the line to User, twice, and in the database's words rather than the model's.
Missing multiplicities. A line with no numbers says nothing about how many.
Multiplicities read the wrong way round. Read from one class across the line to the far end.
The Class Diagram
Composition everywhere. A menu item is not part of an order; it exists without one.
A class for every screen or table, such as MenuPage or OrderForm, in a domain model.
Subclasses for roles, when the only difference between the "subclasses" is what they are allowed to do.
Operations with no rule behind them, such as a getter and a setter for every attribute. They add ink and no information.
Quick revision
- A class: name, attributes, operations. Attribute:
visibility / name : type [multiplicity] = default. - Visibility:
+public,-private,#protected,~package. A slash marks a derived attribute. - Association with a multiplicity at each end, read across the line; an open arrowhead marks navigability.
- Composition (filled diamond): the part is in at most one whole and dies with it. Aggregation (hollow diamond): looser, meaning left to the modeller.
- Generalisation: hollow triangle to the general class. Dependency: dashed arrow from the user to the used.
- A domain model describes the problem's things; a design model describes the code's classes.
Questions you must be able to answer
1. What does a class diagram show? The classes of a system, with their attributes and operations, and the relationships between them: associations with multiplicities, aggregation and composition, generalisation and dependency. It shows structure that is true at every moment, not what happens in any one scenario.
2. Explain the multiplicities on the association between User and Order in the worked diagram. There is a 1 at the User end and 0..* at the Order end. Reading across the line, a user places any number of orders, including none, and every order is placed by exactly one user.
3. What is the difference between aggregation and composition? Which is used between Order and OrderItem, and why? In composition the part belongs to at most one whole at a time and is deleted with it; aggregation is a looser whole-and-part whose exact meaning UML leaves to the modeller. Order and OrderItem use composition, because an order item cannot exist without its order and never belongs to two.
4. Why is there no Owner subclass of User? Because owners, counter staff and students hold the same data and differ only in what they are permitted to do. A difference in permission is represented by the value of the role attribute, an enumeration; a subclass would be needed only if an owner held different data or behaved differently as an object.
5. Why does OrderItem store a unit price when MenuItem already has a price? Because prices change. The unit price is copied into the order item when the order is placed, so that an order keeps the price the student agreed to, whatever the menu says later. The order's total is stored for the same reason, although it could be derived.
The Class Diagram
6. The worked backend has no Order class. Is its class diagram still correct? Yes. The diagram is a domain model of what the system knows and does; the code keeps the data in plain objects and the rules in functions, and each operation in the diagram corresponds to a function in the code, such as canMoveTo to canMove and takeStock to the store's takeStock.
The rest of this subject
These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.