Grade by Grade
C. Designing Modular Programs · Modular and Object-Oriented Design

Object-Oriented Design with CRC Cards and UML

C1.1 C1.2About 75 minutes
Learning goals
  • I can find candidate classes, attributes and methods in a problem description using noun and verb analysis.
  • I can write CRC cards (Class, Responsibilities, Collaborators) and test them by walking through a scenario.
  • I can draw a UML class diagram with visibility, types, associations, aggregation, composition, inheritance and multiplicities.
  • I can turn a UML class diagram into a working Python skeleton.

Words to know

object-oriented design (OOD)
Planning a program as a set of classes whose objects collaborate, before writing the code.
CRC card
An index card listing a Class, its Responsibilities (what it knows and does) and its Collaborators (classes it works with).
responsibility
Something a class knows (data) or does (behaviour) on behalf of the system.
collaborator
Another class whose objects a class needs to fulfil its responsibilities.
UML
Unified Modeling Language: a standard set of diagrams for designing software, including class diagrams.
association
A relationship in which objects of one class refer to objects of another.
aggregation / composition
Whole-part relationships. In composition the whole owns its parts and they do not exist without it; in aggregation the parts can exist on their own.
multiplicity
How many objects take part in a relationship, written like 1, 0..1, 0..∗ or 1..∗.
scenario walk-through
Testing a design by acting out one use of the system, card by card, to check every responsibility has an owner.

Design before you type

A ski and snowboard rental shop near Mont-Tremblant asks you for a program. Before writing a single class, a professional asks: which objects will this program need, what is each one responsible for, and how do they work together? Getting this wrong is expensive: moving a responsibility from one class to another after 3000 lines are written means rewriting many of them.

Expectation C1.1 asks you to decompose a problem into classes using an object-oriented design methodology such as CRC cards or UML. This lesson uses both, in the order many teams do: cards first (cheap, fast, easy to change), then a UML diagram (precise), then code.

Remember from Grade 11

In ICS3U you read UML class boxes (name, attributes, methods; + public, - private) and drew structure charts and data flow diagrams. A structure chart decomposes a program into functions; today you decompose it into classes. Both are kinds of decomposition, and real systems use both: classes on the outside, well-decomposed methods inside.

Step 1: Noun and verb analysis

Write the requirements in plain sentences, then underline the nouns and circle the verbs.

A rental shop rents skis, snowboards and helmets to customers. Each item has an ID, a size and a daily rate. A customer can have several rentals at once. A rental records which item, which customer and the number of days, and computes its cost. The shop keeps an inventory and can list the available items, and it refuses to rent an item that is already out.

  • Nouns that have their own data and behaviour become classes: Shop, Item, Customer, Rental.
  • Nouns that are simple values become attributes: ID, size, daily rate, days.
  • Synonyms merge: inventory is just the shop's collection of items. Skis, snowboards and helmets are kinds of item (an attribute for now; in the inheritance lesson they could become subclasses).
  • Verbs become methods, placed on the class that has the data needed: rent and list go to Shop; compute cost goes to Rental, which knows the item and the days.

Step 2: CRC cards

Take one index card per candidate class. Across the top, write the class name. On the left, list its responsibilities: what it knows and what it does. On the right, list its collaborators: the classes it must work with. CRC cards were introduced by Kent Beck and Ward Cunningham in 1989 as a way to teach object-oriented thinking, and teams still use them because a card is small: if a class's responsibilities do not fit on one card, the class is doing too much.

ItemResponsibilitiesCollaboratorsknow its ID, kind, size, rateknow if it is availableCustomerResponsibilitiesCollaboratorsknow its nameknow its rentalstotal what it owesRentalRentalResponsibilitiesCollaboratorsknow item, customer, dayscompute its costItemCustomerShopResponsibilitiesCollaboratorskeep the inventorylist available itemsrent an item (refuse if out)record every rentalItemRentalCustomer
Four CRC cards for the rental shop. The left column says what each class knows and does; the right column names the classes it works with to do it. Item needs no collaborators: it only knows about itself.

Step 3: Walk through a scenario

Pick a real use of the system and act it out, passing the cards around: "Maya rents skis S1 for 3 days."

  1. Shop receives the request. It finds Item S1 in its inventory (Shop knows the items) and asks Item whether it is available.
  2. Item says yes. Shop marks it unavailable and creates a Rental (item, customer, 3 days).
  3. Shop gives the Rental to Customer Maya, who adds it to her rentals, and keeps its own record.
  4. At checkout, Maya totals what she owes by asking each Rental for its cost; each Rental multiplies its item's daily rate by its days.

If you reach a step that no card is responsible for, add a responsibility. If one card is doing everything, split it. A class that does everything is called a god class, and it is a classic design smell.

Step 4: The UML class diagram

When the cards survive a few scenarios, draw the precise version. A UML class diagram adds:

  • Types for attributes and return values: daily_cents: int, cost(): int.
  • Visibility: + public, - private (internal).
  • Relationships between classes, drawn as lines with special ends (see the notation chart).
  • Multiplicities at each end of a line. Read them from the other side: "one Customer has 0..∗ Rentals" and "each Rental belongs to exactly 1 Customer".
associationa Rental refers to an Itemaggregationa Shop has a collection of Rentalscompositiona Shop owns its Items (parts die with the whole)inheritanceSkis is a kind of Item (next lessons)dependencya class uses another briefly, e.g. as a parameter
UML relationship notation. The symbol always sits at the end of the line next to the whole (for diamonds) or the parent (for the inheritance triangle).
Shop-_items: dict-_rentals: list+add_item(item)+available(kind): list+rent(id, cust, days)Item+item_id: str+kind: str+size+daily_cents: int+available: bool(none)Customer+name: str+rentals: list+total_owed(): intRental+item: Item+customer: Customer+days: int+cost(): int10..∗owns10..∗records0..∗10..∗1
A UML class diagram for the shop. - marks internal members and + public ones. The filled diamond says Shop owns its Items (composition); the open diamond says Shop keeps a collection of Rentals (aggregation); plain lines are associations. Numbers are multiplicities: one Customer can have 0..∗ (zero or more) Rentals, and each Rental has exactly 1 Customer and 1 Item.
Worked example

Example 1: Classes from a description

A school's winter coat drive collects coats from donors. Each coat has a size and a condition. Volunteers sort coats into bins by size. A family can request coats in certain sizes, and a volunteer fills the request from the bins. Find the classes, their key attributes and methods, and one relationship with multiplicities.

  1. Nouns: drive, coat, donor, size, condition, volunteer, bin, family, request. Simple values (size, condition) become attributes of Coat.
  2. Candidate classes: Coat (size, condition), Bin (size, list of coats), Request (family name, list of sizes wanted), CoatDrive (the bins, the requests).
  3. Do we need Donor, Volunteer and Family classes? Only if the program must store data about them or they have behaviour. If it only records names, an attribute such as family_name on Request is enough. Classes that do nothing are noise.
  4. Verbs: collect becomes CoatDrive.add_coat(coat), which puts the coat in the right Bin; fill becomes CoatDrive.fill(request), which takes coats from bins.
  5. Relationship: a Bin aggregates 0..∗ Coats, and each Coat is in at most 1 Bin (0..1, since a coat that has been given away is in no bin).

Classes CoatDrive, Bin, Coat, Request; for example Bin 0..1 ◇—— 0..∗ Coat (aggregation), with methods add_coat and fill on CoatDrive. Answers can differ in detail; what matters is that every responsibility has exactly one owner.

Worked example

Example 2: From the diagram to Python

Write Python classes that match the rental shop UML diagram exactly, then run Maya's scenario: rent skis S1 and helmet H1 for 3 days each, list the skis still available, total what she owes, and show what happens if Leo tries to rent S1.

  1. Each UML box becomes a class. Each attribute becomes a self. attribute set in __init__; private ones get an underscore.
  2. An association becomes an attribute holding a reference: Rental stores self.item and self.customer, the actual objects.
  3. A 0..∗ end becomes a list (or dict): Customer has self.rentals = []; Shop stores its items in a dict keyed by ID so it can find one quickly.
  4. Each method goes on the class that owns the data it needs, exactly as the CRC cards said: Rental.cost() uses its item's rate; Customer.total_owed() asks each of its rentals.
  5. Costs in cents: skis 3500 × 3 = 10 500; helmet 800 × 3 = 2400; total 12 900 cents = $129.00.

The full program is below. The scenario output matches the walk-through: S2 is the only ski left, Maya owes $129.00, and Leo is refused.

class Item:
"""One piece of rental equipment."""
def __init__(self, item_id, kind, size, daily_cents):
self.item_id = item_id
self.kind = kind
self.size = size
self.daily_cents = daily_cents
self.available = True
class Customer:
"""A person who rents equipment."""
def __init__(self, name):
self.name = name
self.rentals = []
def total_owed(self):
"""Return the total cost of this customer's rentals, in cents."""
total = 0
for rental in self.rentals:
total += rental.cost()
return total
class Rental:
"""One item rented by one customer for a number of days."""
def __init__(self, item, customer, days):
self.item = item
self.customer = customer
self.days = days
def cost(self):
"""Return the cost in cents."""
return self.item.daily_cents * self.days
class Shop:
"""The rental shop: owns the inventory and records every rental."""
def __init__(self):
self._items = {} # item_id -> Item
self._rentals = []
def add_item(self, item):
self._items[item.item_id] = item
def available(self, kind):
"""Return the IDs of available items of one kind."""
ids = []
for item in self._items.values():
if item.kind == kind and item.available:
ids.append(item.item_id)
return ids
def rent(self, item_id, customer, days):
"""Rent an item to a customer; return the new Rental."""
item = self._items[item_id]
if not item.available:
raise ValueError(f"{item_id} is already rented")
item.available = False
rental = Rental(item, customer, days)
customer.rentals.append(rental)
self._rentals.append(rental)
return rental
shop = Shop()
shop.add_item(Item("S1", "skis", 160, 3500))
shop.add_item(Item("S2", "skis", 170, 3500))
shop.add_item(Item("H1", "helmet", "M", 800))
maya = Customer("Maya")
shop.rent("S1", maya, 3)
shop.rent("H1", maya, 3)
print(shop.available("skis"))
print(f"{maya.name} owes ${maya.total_owed() / 100:.2f}")
try:
shop.rent("S1", Customer("Leo"), 1)
except ValueError as err:
print("Sorry:", err)

rental.py: four classes that follow the UML diagram. In a real project each class could go in its own module.

['S2']
Maya owes $129.00
Sorry: S1 is already rented

Output of the program above (what Python prints).

Just for fun

Design riddle: what do you call a class with 47 responsibilities that collaborates with every other card on the table?

A god class. Unfortunately, it does not answer prayers; it only answers bug reports.

Hands-on activity

CRC role-play at the kitchen table

You need: 6 to 10 index cards (or paper cut into quarters), a pen, and one or two family members.

  1. Choose a system you know: a food bank's pickup schedule, a skating rink's ice-time booking, or a pizza order app.
  2. Write a short requirements paragraph (4 to 6 sentences). Do noun and verb analysis on it.
  3. Make one CRC card per candidate class. Give each card to a person (one person can hold several).
  4. Act out two scenarios aloud, for example "a team books the rink for Saturday 7 p.m." and "a second team tries to book the same slot". Whoever holds a card speaks for that class and may only do what is written on it.
  5. Fix the cards whenever someone says "Who does that?" Then draw the UML class diagram with multiplicities.

What to look for: no card has more than about 5 responsibilities; every step of each scenario has an owner; the diagram's multiplicities match how the scenario actually worked.

Your turn

1

What do the letters C, R and C stand for on a CRC card?

2

In noun and verb analysis, what usually becomes a class, what becomes an attribute, and what becomes a method?

3

Read this multiplicity aloud: Team 1 ———— 5..∗ Player.

4

What is the difference between composition and aggregation? Give an example of each.

5

Why does cost() belong in Rental rather than in Shop?

6

What is a god class, and how do CRC cards help you avoid one?

7

Convert this UML attribute and method into Python: -_level: int and +level_up(): None.

More practice: warm-up

1

In UML, what does + before a member mean?

2

Which UML symbol shows inheritance?

3

What does the multiplicity 0..1 mean?

4

What are the three compartments of a UML class box?

5

Who introduced CRC cards?

More practice: on your own

1

Find the classes in: "A music streaming app has users. Each user creates playlists. A playlist has a name and an ordered list of songs. A song has a title, an artist and a length in seconds. Users can add songs to playlists and play a playlist."

2

Write the CRC card for Playlist from the previous question.

3

Draw (in words) the UML relationships and multiplicities for User, Playlist and Song.

4

Write the Python skeleton for Playlist with a total_seconds() method, assuming Song has a seconds attribute.

5

During a CRC walk-through for the rental shop, someone asks: "Who checks that a helmet's size fits the customer?" Where would you put that responsibility, and why?

Challenge

1

Add a return_item(item_id, days_late) responsibility to the shop design. Late returns cost an extra half day's rate per late day. Which classes change, and how? Write the new method(s).

2

Your teammate's design has one class, RinkSystem, with 22 methods covering bookings, payments, ice resurfacing schedules and staff shifts. Propose a better decomposition and justify it.

3

UML has many diagram types besides class diagrams. Name two others and say what each one shows.

Check your understanding

Quiz

1.

On a CRC card, what goes in the right-hand column?

2.

In noun and verb analysis of a requirements paragraph, verbs usually become:

3.

In UML, which relationship is shown with a filled (solid) diamond?

4.

Customer 1 ———— 0..∗ Rental means:

5.

An association Rental ———— Item is usually implemented in Python as:

6.

Why do teams often use CRC cards before UML diagrams?

For parents and teachers

Big idea: Expectation C1.1 asks students to decompose a problem into modules, classes or abstract data types using an object-oriented design method such as CRC cards or UML. Design happens before coding: noun and verb analysis, CRC cards, scenario walk-throughs, then a UML class diagram, then code.

How to help: The CRC role-play is genuinely fun with two or three people. Hold a card, and only do what your card says. Ask questions like "Who is responsible for checking that?" Design answers can differ; what matters is that every responsibility has one clear owner and the reasons are sound.

Common difficulties:

  • Making a class for every noun, including simple values such as "size" or "date".
  • Putting all the behaviour in one big controller class (a god class).
  • Reading multiplicities from the wrong end. Always read across the line: "one Customer has 0..∗ Rentals".
  • Mixing up aggregation (open diamond) and composition (filled diamond).