Worked examples — UML — use case, class, sequence, activity, state machine, component diagrams
The scenario matrix
Before solving, let us enumerate every class of case. Think of this like a physics problem where you must cover all quadrants and all signs — here the "quadrants" are the different modelling choices that flip your answer.
| # | Case class (the "axis") | The two extremes / edge you must decide between | Which diagram |
|---|---|---|---|
| C1 | include vs extend | mandatory sub-behaviour vs optional add-on | Use Case |
| C2 | aggregation vs composition | part survives the whole vs part dies with it | Class |
| C3 | multiplicity edge cases | 0..1 (optional), * (many), 1..* (at least one) |
Class |
| C4 | sync vs async message | caller waits vs caller fires-and-forgets | Sequence |
| C5 | alt / opt / loop fragment | branch, conditional, repeat inside one scenario | Sequence |
| C6 | decision vs fork | choose ONE path vs run ALL paths in parallel | Activity |
| C7 | degenerate / self-transition | event that does NOT change state | State Machine |
| C8 | which family & viewpoint | structural vs behavioural — a real-world word problem | any |
Below, eight worked examples, each labelled with the cell it covers. Read the "Forecast:" line and guess before scrolling.
Example 1 — Cell C1 (include vs extend)
Step 1 — Classify each sub-behaviour as always vs sometimes. Why this step? The single deciding question for use-case factoring is: does the base always do this, or only conditionally? "Calculate tax" happens on every checkout → mandatory. "Apply coupon" happens only if a coupon exists → conditional.
Step 2 — Assign the stereotype.
Why this step? Mandatory shared behaviour = <<include>>; optional conditional behaviour = <<extend>>. So Checkout <<include>> Calculate Tax, and Apply Coupon <<extend>> Checkout.
Step 3 — Get the arrow directions right.
Why this step? This is the classic trap (see the parent's mistake callout). The <<include>> arrow points from base → included (Checkout depends on Calculate Tax). The <<extend>> arrow points from extension → base (Apply Coupon reaches into Checkout). See the figure — note the two arrows point opposite ways even though both are dashed.

Verify: Delete "Apply Coupon" — does checkout still make sense? Yes (it was optional) → confirms <<extend>>. Delete "Calculate Tax" — is checkout now incomplete? Yes → confirms <<include>>. Sanity check passes.
Example 2 — Cell C2 (aggregation vs composition)
Step 1 — Apply the lifecycle test. Why this step? The parent gave us the one discriminating question: "If I delete the whole, must the part die too?" For the Playlist: delete it, songs survive → part is independent. For the Invoice: delete it, line items die → part is owned.
Step 2 — Map the answer to a diamond. Why this step? Independent part = aggregation = hollow ◇. Owned part = composition = filled ◆. So Playlist ◇— Song, Invoice ◆— LineItem.

Verify: Cross-check with reality. A song can belong to many playlists at once — only possible if it is not owned by one → aggregation is consistent. A LineItem never belongs to two invoices → exclusive ownership → composition is consistent. ✔
Example 3 — Cell C3 (multiplicity edge cases)
Step 1 — Translate English quantities to multiplicity symbols.
Why this step? Multiplicity is just a range min..max. "none or one" = . "at least one" = (the * means "unbounded"). "any number including zero" = (short for ).
Step 2 — Place the label at the FAR end.
Why this step? A multiplicity written near a class says "how many of this class relate to one of the other". So Person "1" —— "0..1" Passport reads: one person, zero-or-one passport.

Verify: For the many-to-many Student * —— * Course, read it both directions: "one student → many courses" ✔ and "one course → many students" ✔. Both ends are *, which is the fingerprint of a many-to-many → correct.
Example 4 — Cell C4 + C5 (sync vs async, and an alt fragment)
Step 1 — Classify each message.
Why this step? Cell C4: a message is synchronous (solid filled arrowhead, caller blocks) if we need the answer before continuing. charge() must return success/failure before we decide what to show → synchronous. The email is fire-and-forget → asynchronous (open arrowhead).
Step 2 — Wrap the outcome in an alt fragment.
Why this step? Cell C5: two mutually exclusive outcomes (success / failure) in one scenario = an alt combined fragment with two compartments guarded by [ok] and [else].
Step 3 — Order top-to-bottom.
Why this step? In a sequence diagram time flows downward. charge() happens first (we wait for its return), the async email next, then the alt.

Verify: Trace the "declined" path: charge() returns fail → alt picks [else] → "Declined" shown. The email was fired regardless (it is above the alt, outside it), which matches "send receipt" being independent — but wait, do we email a declined payment? The spec said fire the email after the charge call unconditionally, so the model matches the spec. (In an exam, flag this as a design smell.) ✔ structurally correct.
Example 5 — Cell C6 (decision vs fork) with a figure
Step 1 — Find the exclusive choice → decision.
Why this step? "fails vs passes" are mutually exclusive — exactly one path is taken. That is a decision node ◇ with guards [fail] and [pass].
Step 2 — Find the parallel work → fork/join. Why this step? Unit tests and lint run simultaneously and are independent → a fork ━ splits into both, and a join ━ waits for both before Deploy. This is the difference the parent stressed: decision = pick one; fork = do all.
Step 3 — Order: decision first, fork inside the [pass] branch.
Why this step? We only parallelise after a successful build, so the fork lives on the pass branch.

Verify: Count exits. The decision has one active outgoing edge at runtime (either fail or pass) — correct for "choose one". The fork has two active outgoing edges at once — correct for "do all". If you accidentally used a fork for build-pass/fail you'd try to run both success and failure handling, which is nonsense. Shapes match semantics. ✔
Example 6 — Cell C7 (degenerate / self-transition) with a figure
Step 1 — List states and the "real" transition.
Why this step? One object, its rest-conditions are the states. The genuine state change is Draft --publish--> Published.
Step 2 — Identify self-transitions (the degenerate cell).
Why this step? A self-transition is an event that fires but does not change the state. edit in Draft: state stays Draft, an action runs. publish in Published: nothing meaningful happens → a self-loop (or you may simply omit it to say "ignored"). Both are the "zero-input" degenerate case: motion in the event-space, no motion in the state-space.
Step 3 — Decide: draw the self-loop or drop it? Why this step? Drawing it documents "this event is handled here and intentionally does nothing"; dropping it means "undefined/ignored". Being explicit is safer for exams.

Verify: Reachability check — can we reach every state from Draft (the initial)? Draft →(publish)→ Published ✔. Are there dead ends with no way out and no final? Published has no unpublish here, so it is terminal for this spec — acceptable if that is the requirement. Self-transitions correctly leave the state count unchanged (still 2). ✔
Example 7 — Cell C8 (real-world word problem: pick the family & viewpoint)
Step 1 — Structural vs behavioural. Why this step? The parent's grand split: are we describing what exists (static shape) or what happens over time? "Deployable pieces and their dependencies" is a static packaging question → structural.
Step 2 — Choose among structural diagrams. Why this step? Structural = {Class, Component}. Class describes code-level types/attributes; Component describes deployable units and their interfaces/dependencies. "Web front end, inventory service, database" are deployable modules → Component diagram. This is really a Software Architecture question.
Step 3 — Justify the rejections. Why this step? An exam wants you to rule out the others. Sequence/Activity/State = behavioural → wrong family. Use Case answers "who does what", not "how it's packaged". Class is too fine-grained (it shows fields/methods, not deployables).
Verify: Test the choice against the client's exact words: "deployable pieces" → components ✔; "which depends on which" → dependency arrows between components ✔. The Component diagram answers both; no other diagram answers both. ✔
Example 8 — Cell C5 stress (loop fragment + degenerate empty case)
Step 1 — Choose the fragment.
Why this step? "Repeatedly until a condition" = a loop combined fragment, guarded [more pages]. Cell C5 again, but the loop flavour.
Step 2 — Reason about the limiting value: zero iterations.
Why this step? Always cover the degenerate input. If the guard [more pages] is false on the first check, the loop body executes zero times — the messages inside never fire. A well-drawn loop correctly represents "0 to many" repetitions, unlike a plain arrow which implies "exactly once".
Step 3 — State the loop bound.
Why this step? You may annotate loop(0,*) meaning "minimum 0, maximum unbounded", making the zero-page case explicit and exam-proof.
Verify: Substitute the extreme: empty site ⇒ guard false ⇒ 0 body executions ⇒ scraper immediately finishes with no getPage() calls — matches "no more pages". Substitute many pages ⇒ body repeats per page ⇒ correct. Both limits behave. ✔
Recall
Recall Which stereotype is mandatory, and which way does its arrow point?
<<include>> ::: mandatory (always used); arrow points from base → included.
Recall One-question test for aggregation vs composition?
"If I delete the whole, must the part die?" ::: Yes → composition ◆; No → aggregation ◇.
Recall Multiplicity for "at least one" vs "any number"?
at least one ::: ; any number ::: (i.e. ).
Recall Decision ◇ vs Fork ━ — how many outgoing paths are active at runtime?
Decision ::: exactly ONE (guarded, exclusive). Fork ::: ALL (parallel).
Recall A sequence message where the caller does not wait uses which arrowhead?
Asynchronous ::: open (unfilled) arrowhead.
Recall What does a
loop fragment do when its guard is false on the first check?
Runs the body ::: zero times (0-to-many, unlike a plain single arrow).