Chapter Roadmap (1 of 3)
- 2.1 Objects: their knowledge, behaviour and identity
- Four small classes, three kinds of identity, mutable and immutable objects.
- 2.2 Who is responsible for keeping things true?
- The half-open period convention and who protects which condition.
- 2.3 Keeping state safe: encapsulation and information hiding
- Why
privateis not enough, and whatList.copyOfdoes.
- Why
- 2.4 Is a long method the problem? Cohesion and coupling
- Diagnosing a method by its reasons to change, then one modest refactoring.
Chapter Roadmap (2 of 3)
- 2.5 Has-a or is-a? Composition and inheritance
- Use versus subtype, and why a hierarchy is not the default.
- 2.6 Drawing the design: UML class diagrams to read, write and check
- The notation the midterm assesses, so it gets the most time.
- 2.7 Making a change someone else can review
- A small, reviewable change and the commands that inspect it.
- 2.8 Checking an AI’s explanation of the coordinator
- Classifying the claims of a plausible but flawed explanation.
Chapter Roadmap (3 of 3)
- 2.9 Mistakes that look like good design
- Eight misconceptions, each with a better question.
- 2.10 Try it yourself: improve the design, with evidence
- Parts A to D: trace, diagnose, change, organise the team.
- 2.11 Questions to think with
- Room-booking coordinator in a different domain.
- What to take away, and what comes next
- What carries into Chapter 3 (Agile development and the nature of change).
Learning Outcomes (1 of 2)
By the end of the chapter, you should be able to:
- Assign responsibilities to objects and explain the state they protect.
- Distinguish encapsulation and information hiding from mechanically adding getters and setters.
- Diagnose cohesion and coupling problems using concrete change scenarios.
Learning Outcomes (2 of 2)
- Compare composition with inheritance and justify a modest design choice.
- Draw and interpret a small UML class diagram that matches the code, using associations, multiplicity, aggregation, composition, inheritance and interface realisation appropriately.
- Make a behaviour-preserving improvement and present it as a reviewed Git change.
How to Use This Chapter
- Core reading: Sections 2.1 to 2.9.
- Section 2.6 (UML class diagrams) is the notation the midterm assesses, so give it the most time.
- What you must do: the Section 2.8 AI activity and Parts A to D of Section 2.10.
- Parts A to C run on the book’s project; Part D concerns your team’s project.
- If you want more: the optional investigation at the end of 2.10, then the questions and the transfer challenge.
Build on Chapter 1, Use the Companion
- This chapter builds on Chapter 1’s reservation-rule example.
- You should be able to trace conditions, call methods and explain a returned value.
- Java’s official class tutorial gives targeted syntax revision. Appendix A has a short refresher.
- The companion holds every complete source file, a starting version, a completed refactoring, checks and suggested answers.
- Use its starting listings first. Consult solutions after attempting the activity.
Toolchain Baseline
- Java 17 language and API baseline. No preview features, no external dependencies.
- A JDK is required to compile the examples.
- JUnit, Maven and CI configuration are not prerequisites for this first design exercise.
- They are introduced with the relevant later work. Appendix B records the toolchain and commands for the whole book.
Weekly Reading and the Project Version
- Reading: Martin, Agile Software Development: Principles, Patterns, and Practices. The Part I introduction and the object-oriented design fundamentals. Sections 2.1 to 2.5 are the practical counterpart.
- Starting version: the Chapter 2 starter in the companion.
- Resulting version: the same code after the confirmation extraction. It becomes the running baseline for later chapters.
- The fault demonstrations and the notation examples in 2.6 are separate teaching material. They do not join the project.
Opening Scenario: A Demonstration That Works
- The equipment officer can now try a small reservation application.
- It checks a borrower’s limit, looks for a conflicting reservation, records a successful request and returns a confirmation message.
- The demonstration works.
- But the developer who wrote it is absent when another team member is asked to change the message.
Three Questions to Carry Through the Chapter
- Why is the conflict check concerned with an equipment item while the count is concerned with a borrower?
- Could the list of stored reservations be changed from outside the class?
- Which responsibilities belong together, and how should other code be allowed to use them?
- Knowing how to declare a class is necessary. It does not answer these.
Identifiers Are Different Kinds of Identity
- Two physical cameras may share a model name but need different item identifiers.
- Two reservations for the same camera on different dates are still separate reservations.
- A borrower’s name is a poor identifier: names can change or be shared by different people.
- The identifiers stand in for borrower and equipment records. A non-blank identifier does not prove a real person or item exists.
Java Object Identity
- Two references can point to the same instance, or to distinct instances that represent the same domain identifier.
==on references checks whether they refer to the same object.- Domain equality needs a deliberate definition. It is not automatically established by matching fields.
- Our
Reservationdoes not overrideequals. Do not assume identifier-based equality.
State and Behaviour
- State is information an object holds.
- Behaviour is what it can do through its operations.
- A reservation can expose its identifier and period without letting callers replace them.
- A coordinator can add a new record after checking the request.
- A period can answer whether it overlaps another period.
From the Numeric Rule to a Small System
ReservationRulesstays unchanged: at most three active reservations, one to seven days.- The staff variation remains a future design exercise.
- This version applies the student policy to every borrower identifier.
- The starter has four domain and application classes.
The Four Starter Classes
| Class | Main role | State held |
|---|---|---|
ReservationRules | Evaluate the numeric borrowing policy | No instance fields |
LoanPeriod | Represent a valid period; answer overlap questions | Start date and exclusive end date |
Reservation | Record a confirmed reservation | Three identifiers; a period |
ReservationCoordinator | Process requests using policy and existing records | In-memory reservations; a rules object |
The Starter Files
| File | Role |
|---|---|
ReservationRules.java | The unchanged numeric policy from Chapter 1 |
LoanPeriod.java | Valid dates and exclusive-end overlap semantics |
Reservation.java | Immutable reservation details |
ReservationCoordinator.java | The starting request workflow |
Chapter02Demo.java | Three requests: success, overlap, adjacency |
Chapter02Checks.java | Supplied behavioural checks (34) |
Invariants Belong at Different Boundaries
- An invariant is a condition that must hold for valid objects or valid system states.
- One
LoanPeriodcan ensure its end is after its start. - It cannot, by itself, ensure that another reservation has not already claimed the same equipment.
- Local invariants and system-level conditions need different enforcement points.
A Period Convention We Can Test
- Dates are calendar dates with no time of day.
- A period is start inclusive, end exclusive:
[start, end). - 20 October to 22 October covers the 20th and 21st. Another period may begin on the 22nd.
- This is an explicit teaching policy. A real service might require a cleaning or inspection interval, which would need another rule.
LocalDate and the Convention
- Java’s
LocalDateis immutable and represents a date without a time or time zone. - We use
isBeforeandplusDaysto express the convention. - We do not interpret these dates as UTC instants or fixed numbers of elapsed hours.
- The exclusive-end convention is a project decision, not a rule
LocalDateimposes.
The LoanPeriod Constructor
public LoanPeriod(LocalDate start, LocalDate endExclusive) {
this.start = Objects.requireNonNull(start, "start");
this.endExclusive =
Objects.requireNonNull(endExclusive, "endExclusive");
if (!start.isBefore(endExclusive)) {
throw new IllegalArgumentException("End must be after start.");
}
}- Missing date:
NullPointerException. Zero-length or reversed interval:IllegalArgumentException.
The overlaps Operation
public boolean overlaps(LoanPeriod other) {
Objects.requireNonNull(other, "other");
return start.isBefore(other.endExclusive)
&& other.start.isBefore(endExclusive);
}- Two periods overlap when each begins before the other ends.
- Both comparisons are strict, so adjacent periods do not overlap.
requireNonNullthrows for a null argument. It does not fabricate a replacement period.
The Same Examples as a Table
| First | Second | Overlap? | Why |
|---|---|---|---|
[20 Oct, 22 Oct) | [21 Oct, 23 Oct) | Yes | Both include 21 October |
[20 Oct, 22 Oct) | [22 Oct, 23 Oct) | No | Second begins at the first’s exclusive end |
[20 Oct, 22 Oct) | [20 Oct, 22 Oct) | Yes | Identical periods overlap |
[20 Oct, 22 Oct) | [19 Oct, 24 Oct) | Yes | The second contains the first |
- Comparing start dates alone would miss some of these relationships.
Who Protects Which Condition?
- The constructor protects the local validity of a period.
ReservationRulesprotects the numeric policy, only when given an accurate count.- The coordinator counts its records and checks item conflicts before storing.
- No one class can enforce all of these without information from the others.
Operating Assumptions of the Starter (2 of 2)
- The coordinator checks availability only against its own stored records.
- Maintenance schedules and other coordinators are outside this version.
- Within one sequential coordinator, checking conflicts before adding prevents the demonstrated overlap.
- It does not establish safe concurrent booking by multiple processes. Later integration work revisits that boundary.
Three Related Ideas
- Encapsulation groups state with the operations that govern its use, and controls access to that state.
- Information hiding concerns which implementation decisions callers should not need to know.
- Abstraction presents what is relevant to a caller and leaves out unnecessary detail.
- None is achieved simply by writing
privatebefore every field.
Abstraction Through an Ordinary Method
- A caller can ask a period whether it overlaps another without repeating the date comparisons.
- That abstraction exists through an ordinary public method.
- It does not need Java’s
abstractkeyword or a separate interface. - A useful abstraction still needs a clear contract, including our decision that adjacent periods do not overlap.
How LoanPeriod Avoids This
- The constructor requires both dates together and validates the relationship.
- It provides readers but no setters.
- To express another period, a caller creates another valid object.
- No invalid intermediate state enters the public protocol.
- In another domain a controlled mutation operation could be right. Immutability is a design choice, not a rule.
What List.copyOf Does Not Do
- It does not deep-copy the elements.
- It does not make mutable elements immutable.
- Here,
Reservation,LoanPeriod,StringandLocalDatealready provide the immutability needed for the values we expose. - Be able to explain the protection without claiming all copies are deep copies.
What Remains Exposed
- The coordinator still exposes more representation than a mature system might require: callers know stored reservations can be inspected as a list.
- That is acceptable for a small teaching version.
- If a caller later needs only a count or an availability answer, a narrower operation may reduce what the caller must understand.
Cohesion
- Cohesion concerns how closely the responsibilities within a component belong together.
LoanPeriodhas a focused purpose: its validation and overlap calculation concern the same period concept.- A coordinator that also knows message wording, storage details and borrowing policy has more varied responsibilities.
Coupling
- Coupling concerns the dependencies between components.
- A component depends on another when it relies on its type, operations, data format or behaviour.
- Some dependency is necessary: software works because components collaborate.
- The question: does the dependency express a useful relationship, or let unrelated changes spread?
The reserve Method: Validate and Count
public String reserve(String borrowerId, String itemId,
LocalDate start, int requestedDays) {
if (borrowerId == null || borrowerId.isBlank()
|| itemId == null || itemId.isBlank()) {
throw new IllegalArgumentException(
"Identifiers must not be blank.");
}
Objects.requireNonNull(start, "start");
int activeCount = 0;
for (Reservation existing : reservations) {
if (existing.borrowerId().equals(borrowerId)) { activeCount++; }
}The reserve Method: Policy and Conflict
if (!rules.mayRequest(activeCount, requestedDays)) {
return "Rejected: borrower limit or duration.";
}
LoanPeriod requested =
new LoanPeriod(start, start.plusDays(requestedDays));
for (Reservation existing : reservations) {
if (existing.itemId().equals(itemId)
&& existing.period().overlaps(requested)) {
return "Rejected: item unavailable.";
}
}- Policy is rejected before availability; keep that order.
The reserve Method: Store and Confirm
Reservation reservation = new Reservation(
"R" + (reservations.size() + 1), borrowerId, itemId, requested);
reservations.add(reservation);
return "Confirmed: " + reservation.id()
+ " for " + reservation.borrowerId()
+ " on " + reservation.itemId()
+ " [" + reservation.period().start()
+ ", " + reservation.period().endExclusive() + ").";
}- It creates and stores a reservation, then assembles its confirmation. It does not send it.
Diagnosis by Change Scenario (2 of 2)
| Possible request | Decision it affects | Current location |
|---|---|---|
| Store records beyond process lifetime | Storage mechanism | Coordinator’s list and storage operations |
| Translate a confirmation message | Presentation wording | Coordinator’s final return expression |
- This identifies which changes currently require editing the coordinator, and why.
- A future database is a possible boundary. It is not a reason to build a repository framework now.
A Small Improvement with a Limited Claim
- Extract the confirmation expression into a private method
confirmationFor. - The request method still adds the record first, then returns the helper’s result.
- The helper receives the existing reservation and returns exactly the same text.
- This is a modest refactoring: it gives the formatting a name.
What the Extraction Does Not Do
- It does not create a separate presentation component.
- It does not remove storage coupling.
- It does not make the coordinator thread-safe.
- A private helper changes readability inside the class. A new component would change the collaboration between classes.
- If translation later needs collaborators, a separate formatting responsibility may become justified.
Inheritance
- Inheritance defines a derived class in relation to a superclass.
- Java supports inherited members, method overriding and run-time selection of overridden instance methods.
- These mechanisms can express a useful family of related behaviours.
- They also establish expectations that callers may rely on.
A Hypothetical SpecialReservation
- Suppose a subclass is introduced only because some borrowers have longer limits.
- Is the difference really a new kind of reservation, or a policy governing whether one may be created?
- If staff status can change while an existing reservation keeps its identity, forcing that distinction into a hierarchy may be awkward.
Not a Verdict Against Inheritance
- This does not prove inheritance is always unsuitable.
- Ask: what varies, what remains stable, and what a caller expects from the base type.
- Chapter 5 develops behavioural substitutability more formally.
- For this version, constructors, fields and collaborators already express the relationships without a custom hierarchy.
Composition vs. Inheritance: Questions
| Question | Composition can help when | Inheritance deserves consideration when |
|---|---|---|
| What is the relationship? | One object uses or contains another | One type has a meaningful subtype relationship |
| What varies? | A collaborator or delegated responsibility | Behaviour shared through a stable base contract |
| What must be reviewed? | How collaborators are selected and called | Whether derived behaviour preserves caller expectations |
What a Class Diagram Is
- A class diagram is a selective explanation of a design.
- It answers one question for one audience: for example, “which objects take part in a reservation request, and how are they related?”
- It is not a copy of the source code. A diagram showing every field and method answers no question well.
- Class diagrams are the only UML notation used in this chapter.
Reading the Boxes
- Each box is a class with up to three compartments:
- top: the class name,
- middle: the attributes (data the class keeps),
- bottom: the operations (methods other code can call).
- A label in double angle brackets above the name, such as
<<interface>>, says what kind of class it is. +marks a public member and-a private one.
A Box and the Java It Describes
// Excerpt from LoanPeriod.java; method bodies omitted.
public final class LoanPeriod {
private final LocalDate start;
private final LocalDate endExclusive;
public LoanPeriod(LocalDate start, LocalDate endExclusive) { ... }
public LocalDate start() { ... }
public LocalDate endExclusive() { ... }
public boolean overlaps(LoanPeriod other) { ... }
}Mapping Box Parts to Source
| Part of the box | In the Java source | How to read it |
|---|---|---|
LoanPeriod | class LoanPeriod | The class name |
-LocalDate start | private final LocalDate start; | A private attribute start of type LocalDate |
+overlaps(LoanPeriod other) boolean | public boolean overlaps(LoanPeriod other) | A public operation taking a LoanPeriod, returning boolean |
- Type comes before the name, as in Java. The return type comes after the parameter list.
Attribute or Line, Not Both
- A field whose type is a plain value (
String,int,LocalDate) is normally an attribute. - A field whose type is another class of your design is normally a line to that class’s box.
- Do not show it both ways. A
-LoanPeriod periodattribute beside a line toLoanPeriodsays one thing twice.
Reading Multiplicity
- The number at each end of a line is a multiplicity: how many objects take part.
- Stand at one class and read the number at the far end.
- That is how many objects of the far class one object of your class is linked to.
| Written | Means |
|---|---|
1 | exactly one |
0..1 | none or one |
0..* | zero or more |
1..* | one or more |
Try It 1: The Other Two Lines
- Write one sentence for each of the other two lines in Figure 2.1, reading from both ends.
- For each number, find where the Java code enforces it and where the model merely assumes it.
- A strong answer separates “enforced by the code” from “true of how the code happens to be used”.
Try It 1: Answers (1 of 2)
| Line | Reading | Enforced by |
|---|---|---|
Coordinator 1 to 1 Rules | Each coordinator uses one rules object; each rules object is used by one coordinator | private final ReservationRules rules = new ReservationRules(); |
Coordinator 1 to 0..* Reservation | Each coordinator stores zero or more; each reservation is stored by one coordinator | The list starts empty and has no upper limit |
Try It 1: Answers (2 of 2)
| Line | Reading | Enforced by |
|---|---|---|
Reservation 0..* to 1 LoanPeriod | Each reservation refers to exactly one period; a period may be referred to by any number of reservations | requireNonNull(period, "period") and the final field |
- Only assumed:
- that a rules object is never shared,
- that a reservation is held by only one coordinator (the constructor is public; snapshots hold the same objects),
- the
0..*on periods (nothing prevents sharing, though each reservation currently gets its own).
Relationships: When to Use Each (1 of 2)
| Relationship | Read it as | Use it when |
|---|---|---|
| Association | One object is linked to another | The link is structural and lasting |
| Dependency | One class uses another without keeping it | A parameter or brief call is the only link |
| Aggregation | A whole groups parts that can exist without it | Parts can be shared or removed and still exist |
Relationships: When to Use Each (2 of 2)
| Relationship | Read it as | Use it when |
|---|---|---|
| Composition | A whole owns parts with no meaning without it | A part’s lifetime is tied to its owner |
| Inheritance | The subclass is a kind of the superclass | A real subtype relationship holds |
| Interface realisation | The class implements the interface’s contract | Several classes satisfy one contract callers rely on |
Reading the Sampler
- A pack owns its pages: a page has no meaning without its pack (composition).
- A pack only collects articles, which exist independently and can appear in many packs (aggregation).
- Articles and authors: a plain association, no arrowheads, because the description does not say who holds the reference.
- This is a notation sampler for a course-pack printing service. It is not part of the reservation project.
Choosing a Relationship by Meaning
Choose by what it means, not how it looks. For each pair of classes, ask in order:
- Does one class implement a contract that other classes rely on? Realisation.
- Is A a kind of B, so a client of B could be handed an A without noticing? Inheritance (Chapter 5 tests this with the Liskov Substitution Principle).
- Does A keep a lasting reference to B in a field? If not, and A only uses B as a parameter, local variable or short-lived object: dependency.
Choosing a Relationship, Continued
- If A keeps the reference, does B belong to A so completely that it has no meaning without A and is discarded with it? Composition.
- Does A group Bs that exist independently and could be shared or removed? Aggregation, but only if you can say what the grouping adds. Otherwise a plain association.
Aggregation and Composition Are Claims
- Aggregation is often indistinguishable from association in code.
- Composition describes a lifetime the Java types do not enforce.
- Both are claims about meaning.
- Figure 2.1 uses plain associations: periods can be shared by reference, and the code enforces no lifetime.
- Do not confuse “composition” as a programming idea (building from collaborators) with the filled diamond, a stronger ownership claim.
From Code to Diagram (1 of 2)
| In the Java source | In the diagram |
|---|---|
private final LocalDate start; | Attribute -LocalDate start |
private final List<Reservation> reservations | Association to Reservation, 0..* at that end |
private final ReservationRules rules | Association to ReservationRules, 1 at that end |
A parameter, local variable or new X() used only inside a method | Dependency on X |
From Code to Diagram (2 of 2)
| In the Java source | In the diagram |
|---|---|
class A extends B | Inheritance, triangle at B |
class A implements I | Realisation, triangle at I |
public and private | + and - |
- Caution 1: the Java type does not settle the meaning. A
List<Reservation>might be an association or a composition. Only the design decides. - Caution 2: a field that can be
nullis0..1, not1.
A Worked Example: From Scenario to Diagram
The workshop keeps tools in numbered lockers.
- A locker holds at most one tool, and a tool sits in at most one locker.
- A technician looks after any number of lockers; each locker has exactly one technician at a time, and can be reassigned.
- Every inspection of a locker is logged as an entry with a date and a note; an entry is meaningless without its locker.
- Reports are printed as plain text now; other formats are expected later.
Steps 1 and 2: Nouns, Then Classes
- Step 1: find the nouns. Workshop, tools, lockers, technician, inspection, entry, date, note, report, plain text, formats.
- Step 2: decide which become classes. Keep a noun as a class when it has data or rules the description needs.
- The table on the next slides records each decision and its reason.
Step 2: Candidates and Decisions (2 of 2)
| Candidate | Decision | Reason |
|---|---|---|
| Inspection entry | Class | Has a date and a note; belongs to a locker |
| Date, note, number, name | Attributes | Plain values describing a class |
| Report | Operation of Locker | Something a locker does, not a thing with its own data |
| Plain text, other formats | ReportPrinter interface and PlainTextPrinter class | The format is expected to vary |
Step 3: Attributes and Operations
- Separate what the description states from what you assume.
- Stated: a locker’s number and report; an entry’s date and note.
- Assumptions, which you should label as assumptions:
- a technician’s name and a tool’s name as identifying attributes,
- a single
printoperation on the printer, one reasonable way to give the report a format-independent contract.
- A different, justified choice is acceptable.
Step 4: Choose a Relationship for Each Pair
| Pair | What decides it | Result |
|---|---|---|
| Technician and locker | Neither owns the other; a locker can be reassigned | Plain association |
| Locker and tool | Neither is part of the other | Plain association |
| Locker and inspection entry | An entry is meaningless without its locker | Composition, diamond at Locker |
Locker and ReportPrinter | A locker uses a printer only while reporting | Dependency |
PlainTextPrinter and ReportPrinter | The class implements the contract | Realisation |
Step 5: Write Each Multiplicity as a Sentence
- Each locker is looked after by exactly one technician:
1at theTechnicianend. - A technician looks after any number of lockers:
0..*at theLockerend. - A locker holds at most one tool and a tool sits in at most one locker:
0..1at both ends. - A locker has any number of entries; an entry belongs to exactly one locker.
Checking a Diagram Against the Code
A diagram that disagrees with its code is worse than no diagram, because readers trust it.
- For every box, find the class. For every attribute, find the field with that name and type.
- For every line, find the field, parameter or
extends/implementsclause that creates it. If you cannot, remove the line or fix the code.
Try It 2: What Figure 2.1 Leaves Out
- Find two details present in the Java but absent from Figure 2.1.
- Say why leaving each out is acceptable.
- Examples of acceptable answers: the constructors and their validation; the accessors
id(),borrowerId(),itemId(); theperiodfield (the association line already says it); the privatereservationsandrulesfields; the order of the checks and the exceptions thrown.
What the Diagram Cannot Tell You
- It omits standard library classes and some accessors.
- It does not establish the order of operations, display every validation, or prove overlapping bookings are rejected.
- Read the method body and the period convention for those questions.
- After extracting a private formatting method, the relationships in Figure 2.1 are unchanged: useful evidence about the scale of the edit.
Try It 3: Answers (1 of 2)
| # | Mistake | Why it is wrong |
|---|---|---|
| 1 | -Locker locker attribute in Technician | Repeats the line; implies one locker per technician |
| 2 | Multiplicities reversed on Technician to Locker | Says each locker has many technicians and each technician one locker |
| 3 | Filled diamond at Technician | Claims lockers die with their technician, but a locker can be reassigned |
Try It 3: Answers (2 of 2)
| # | Mistake | Correct notation |
|---|---|---|
| 4 | Locker <|-- InspectionEntry | Composition: filled diamond at Locker, 1 and 0..* |
| 5 | Locker ..> PlainTextPrinter and report(PlainTextPrinter printer) | Depend on ReportPrinter; type the parameter as ReportPrinter |
| 6 | ReportPrinter <|-- PlainTextPrinter | Dashed line, hollow triangle at ReportPrinter |
- Mistakes 2 and 3 sit on the same line. Count them as two ideas if both are explained.
Common Drawing Mistakes (1 of 3)
| Mistake | Why it is wrong | Better habit |
|---|---|---|
| Multiplicity at the wrong end | The far number counts the far class; reversed numbers say the opposite | Read every line aloud in both directions |
| Arrowhead as ownership | An arrowhead shows who holds the reference, not who owns whom | Use diamonds, with a reason, for ownership |
| Inheritance for “uses” or “has” | A locker is not a kind of technician; an entry is not a kind of locker | Test “A is a kind of B” and its promises |
Common Drawing Mistakes (2 of 3)
| Mistake | Why it is wrong | Better habit |
|---|---|---|
| Solid line for realisation | Realisation is dashed; solid with a triangle is inheritance | Check the line style of every triangle |
Filled diamond because the field is final | Composition claims a lifetime; final only fixes a reference | Ask whether the part can exist without the owner |
| Showing a relationship twice | -Locker locker plus a line to Locker repeats one fact | Show a field of a design class as a line only |
Common Drawing Mistakes (3 of 3)
| Mistake | Why it is wrong | Better habit |
|---|---|---|
| Depending on a concrete class when an interface exists | A client naming PlainTextPrinter cannot accept another format | Point the dependency at the interface |
| Drawing every class and member | The design question disappears among the detail | One diagram, one question, only the members it needs |
| A diagram that disagrees with the code | Readers trust the diagram | Run the checking steps before sharing it |
Branches and Pull Requests
- A branch names a line of development. For the lab, use a short-lived branch containing the confirmation extraction.
- A pull request (or merge request) is a hosting-platform review proposal. It is not the same as a local commit.
- A local peer review can still serve the learning objective when no hosting account is available.
Trunk-Based or Feature Branches?
- Trunk-based development: integrate small changes frequently into a main line, sometimes through very short-lived branches.
- Feature-branch workflow: group a feature’s work on a branch before integration.
- The approaches overlap: a short reviewed branch can be part of frequent integration.
- For this course: keep changes small, reviewable and integrated regularly.
Reading the Output Correctly
- An empty
git diffafter staging does not mean there is nothing to commit. - Untracked files also need attention in
git status. - Keep compiled class files out of commits: the companion’s
.gitignorehandles generated files. - Appendix B collects the Git commands and the pull-request workflow (B.8).
A Useful Review Description
- State the problem, the limited change, and the checks performed.
- Example: “The request method interleaves storage and confirmation text. This change moves the text assembly into a private helper. The existing messages and stored records remain unchanged in the supplied checks.”
- Add limits: persistence, concurrent booking, lifecycle operations and message delivery remain outside this version.
Ground Rules for the Activity (1 of 2)
| For this activity | |
|---|---|
| Your goal | Compare an explanation of the coordinator with its actual control flow, state changes and side effects |
| You start with | The complete starting ReservationCoordinator and collaborators |
| What is in bounds | Judge the explanation only; do not change the code |
Ground Rules for the Activity (2 of 2)
| For this activity | |
|---|---|
| How to check | Tracing the code, the three-request demonstration, the supplied checks |
| How to decide | Keep supported claims, reject contradicted ones, mark claims the code does not establish |
| No AI account? | The ready-made example is enough |
- Chapter 15 collects the recurring AI workflows. This activity is self-contained.
The Ready-Made Example (1 of 2)
An ready-made example written by the author imitating a plausible AI explanation. Not a captured response from a real model.
The coordinator validates identifiers, counts the borrower’s stored reservations and checks the numeric rule. It tests availability using existing reservations for the same item. On success, it adds a reservation before returning a confirmation.
The Ready-Made Example (2 of 2)
It sends that confirmation by email and persists the reservation to a database. Because the list field is final, callers receive a deeply immutable copy and booking is safe under concurrent requests.
- Classify each claim as supported, contradicted or not established.
- Cite the relevant operation, or the absence of one.
Claim Classification (1 of 2)
| Claim | Judgement | Evidence |
|---|---|---|
| Validates identifiers | Supported within scope | Rejects null or blank identifiers; no catalogue check |
| Counts the borrower’s stored reservations | Supported | Loops over records, compares borrower identifiers |
| Applies the numeric rule | Supported | Calls ReservationRules.mayRequest |
| Checks conflicts for the same item | Supported | Matching item and overlapping period |
Claim Classification (2 of 2)
| Claim | Judgement | Evidence |
|---|---|---|
| Adds before returning a confirmation | Supported | add is called before the message is built |
| Sends email | Contradicted | No sender or delivery call; the method returns a string |
| Persists to a database | Contradicted | An in-memory ArrayList; no database operation |
| Final list: callers get a deeply immutable copy | Incorrect | final stops reassignment; List.copyOf is shallow |
| Concurrent booking is safe | Not established | Check and add are not one synchronised operation |
A Corrected Four-Sentence Explanation
The coordinator rejects missing or blank identifiers and requires a start date, then counts the borrower’s stored records and applies the numeric rule. It checks overlapping periods for the same item before recording a successful reservation and returning confirmation text.
A Corrected Four-Sentence Explanation, Continued
Policy and availability rejections do not add a record, and the returned reservation list is an unmodifiable snapshot containing immutable elements. The implementation has in-memory storage only, sends no email, and is intended for sequential use rather than concurrent booking.
Optional Live Assistant
Trace this coordinator and its supplied collaborators. Identify inputs, returned values and state changes on success and rejection. State what is established about storage, notifications and concurrency. Separate facts visible in the code from assumptions. Do not change the code.
- Record one accepted claim and one rejected or qualified claim with evidence.
- A second model’s agreement is not a substitute for tracing the operations.
Mistakes 1 and 2
- “Private fields mean the object is safely encapsulated.”
- A returned mutable list can still expose a modification path.
- Better question: what can callers change through the references they receive?
- “A final field makes the referenced object immutable.”
- The coordinator can still add to its final list.
- Better question: is the reference fixed, the object immutable, or both?
Mistakes 3 and 4
- “Every noun should immediately become a class.”
- Several concepts have no behaviour or state we need yet.
- Better question: what responsibility would that class own now?
- “More interfaces automatically mean less harmful coupling.”
- An interface can repeat an unstable contract without solving a caller’s problem.
- Better question: which dependency would this interface improve?
Mistakes 5 and 6
- “Calling another class means inheriting from it.”
- The coordinator uses a policy; it is not a policy subtype.
- Better question: is the relationship use, containment or substitution?
- “A filled diamond just means ‘has a’.”
- Composition claims the part cannot outlive its owner;
LoanPeriodobjects can be shared by reference. - Better question: does the code enforce this lifetime, and would the domain agree?
- Composition claims the part cannot outlive its owner;
Mistakes 7 and 8
- “A formatting refactoring fixes the whole design.”
- The helper leaves storage and other responsibilities in the coordinator.
- Better question: which improvement did this edit actually make?
- “Green checks prove the application is production-ready.”
- The starter omits lifecycle, persistence, catalogue validation and concurrency.
- Better question: which claims have we tested, under which assumptions?
Inputs and Starting Point
- Parts A to C use the book’s project; Part D concerns your team’s project.
- Use the companion’s common source files and the starting
ReservationCoordinator.java. - Save all six Java files in one directory; compile and run them using the companion’s instructions.
- The check runner should report 34 passing checks before you begin.
- Read the checks as provided evidence. You need not master the runner yet.
Compile and Run
javac --release 17 -d out ReservationRules.java LoanPeriod.java \
Reservation.java ReservationCoordinator.java \
Chapter02Demo.java Chapter02Checks.java
java -cp out Chapter02Demo
java -cp out Chapter02Checks- Recompile after every source change. Otherwise the JVM may run old class files and give misleading evidence.
Part A: Understand the Request
Trace the three requests in Chapter02Demo.
- Record the returned message and the number of stored records after each call.
- Identify the first condition that rejects the overlapping request.
- Explain why the adjacent request is accepted.
- Explain why the rejected request does not consume a reservation identifier.
Part A: The Trace
| Call | Outcome | Stored | Important step |
|---|---|---|---|
| B1 requests CAM1 from 20 Oct for two days | Confirmed as R1 | 1 | Empty history has no conflict |
| B2 requests CAM1 from 21 Oct for one day | Rejected: unavailable | 1 | Overlaps R1 for the same item |
| B2 requests CAM1 from 22 Oct for one day | Confirmed as R2 | 2 | Equality with R1’s exclusive end is permitted |
- Identifiers derive from the number of stored records, so a rejection consumes none. That may not suit deletion, persistence or several coordinators.
Part A: Diagram and Invariants
- Draw a small class diagram of the starter without copying Figure 2.1.
- Use the “From code to diagram” table, then run the four checking steps on your own drawing.
- Annotate one local invariant and one condition that needs information from multiple objects.
- A local invariant: a period’s end follows its start (the
LoanPeriodconstructor). - A multi-object condition: confirmed reservations for the same item do not overlap (the coordinator checks it).
Part A: A Separate Modelling Exercise
Equipment kits. It does not extend the project.
- A kit has a name and holds one or more equipment items.
- Removing a kit does not delete its items; an item belongs to at most one kit.
- Each item keeps any number of maintenance records; a record belongs to exactly one item and has no meaning without it.
- A kit summary is produced through an exporter: CSV now, other formats later.
Part A: What to Draw
- A class diagram with the classes, their key attributes and multiplicities.
- Include at least one aggregation, one composition, one interface realisation and one dependency.
- State one assumption you had to make.
- For each relationship, write the Section 2.6 question that decided it.
- Then read every line aloud in both directions against the scenario.
What a Strong Kit Answer Shows
| Requirement | Model decision |
|---|---|
| A kit holds one or more items; removing it does not delete them | Aggregation, 1..* at the items end |
| An item belongs to at most one kit | 0..1 at the kit end |
| A record belongs to exactly one item and has no meaning without it | Composition from item to record: 1 at the item, 0..* at the records |
| A summary is produced through an exporter; other formats expected | Exporter interface realised by CsvExporter; the kit depends on the interface |
Common Kit Errors
- Interface realisation drawn as inheritance with a solid line.
- The diamond reversed.
1..*at the wrong end.- The kit depending directly on
CsvExporter. - An assumption can be anything stated and consistent: for example, that an item with no kit is allowed.
- Do not credit extra classes for their own sake.
Part B: A Model Answer
- The coordinator validates inputs, counts records, invokes policy, checks availability, allocates an identifier, stores a record and formats a message.
- Two reasons to change: a new storage mechanism and changed confirmation wording.
LoanPeriodis cohesive: construction rules and overlap concern one concept.- The coordinator depends on both reservation representation and message wording.
Part C: Make the Change
- On a short-lived branch, move only the confirmation-string expression into a private
confirmationFor(Reservation reservation)method. - Keep unchanged: output, validation order, identifier allocation, storage effects.
- Do not add a new interface or change public method signatures.
- Run the checks again, inspect the diff, ask a peer to review.
Part C: The Completed Method
reservations.add(reservation);
return confirmationFor(reservation);
}
private String confirmationFor(Reservation reservation) {
return "Confirmed: " + reservation.id()
+ " for " + reservation.borrowerId()
+ " on " + reservation.itemId()
+ " [" + reservation.period().start()
+ ", " + reservation.period().endExclusive() + ").";
}Part C: Git Baseline Commands
git init -b main
git add .gitignore ReservationRules.java LoanPeriod.java \
Reservation.java ReservationCoordinator.java \
Chapter02Demo.java Chapter02Checks.java
git diff --staged
git commit -m "Add chapter 2 reservation starter"
git switch -c refactor/confirmation-text.gitignorecontainsout/and*.class.
Part C: Commit the Extraction
git status
git diff
git add ReservationCoordinator.java
git diff --staged
git commit -m "Extract reservation confirmation formatting"
git log --oneline -2
git status- Produce one focused commit after addressing relevant feedback.
- In independent study, perform a self-review and label it honestly as such.
Part C: A Review Record
- Problem: the request method contains message formatting alongside the request workflow.
- Change: move only the confirmation-string construction into
confirmationFor, keeping record creation and storage order. - Evidence: identical demonstration output; the same 34 checks pass; the diff changes only internal organisation.
- Limits: persistence, concurrent booking, lifecycle operations and message delivery remain outside this version.
Part C: What the Review Record Needs
- The change or commit identifier.
- The reviewer.
- The evidence inspected.
- The findings and the response.
- Creating a commit does not create a hosted pull request or establish that anyone reviewed it.
- Record your own observed results. Do not copy a claim you did not verify.
Part D: Organise Collaboration
- Form teams of three or four as directed by the module leader.
- Confirm which provided starter domain your assessed project uses. If it is not equipment lending, use each chapter’s transfer note.
- Set up an issue board with columns that match how your team will work.
- Add three cards with concrete completion conditions.
Part D: Cards and Branch Policy
- A good card: “extract confirmation formatting with unchanged output and checks”. Not: “improve OO”.
- Agree a branch policy:
- very short-lived branches integrated into a main line often, or
- one feature branch per story.
- Write down your reason. The reason matters more than the choice.
- Agree who prepares the next demonstration and who reviews the next change. Rotate roles.
Evidence to Retain
- The annotated diagram and the scenario model.
- The responsibility diagnosis.
- Before-and-after check results.
- The commit identifier and the review note.
- Your AI claim analysis.
- Your team’s issue board and branch-policy note.
- A short limitation statement: which risks remain after the extraction?
Optional Investigation: Break It on Purpose
- In a temporary copy, replace
List.copyOf(reservations)withreservations. - Predict the effect of an external
clear()call, then run the checks. - Result:
snapshot::clearsucceeds; the runner reports thatsnapshot unmodifiableexpected anUnsupportedOperationException. - Restore the original before continuing. This is a fault demonstration, not an improvement.
Concept Questions
- Distinguish a Java object reference from a reservation’s domain identifier. Why are equal identifier strings not sufficient to make two
Reservationinstances equal throughequalsin this starter? - Explain one local invariant and one system-level condition in the project. Why do they need different enforcement points?
- Explain cohesion and coupling using one likely change to the coordinator. Why is reducing the total number of dependencies not a sufficient design goal?
- Compare composition and inheritance for a reservation and its loan period. What would inheritance incorrectly suggest?
Diagnosis Questions
- A developer adds a getter returning the private mutable list and claims the coordinator is still encapsulated because the field is private. Give a concrete counterexample and explain the limits of the proposed correction using
List.copyOf. - A refactoring returns the same confirmation text but omits
reservations.add(reservation). The developer demonstrates the printed text and declares success. Explain the behavioural change and identify evidence that would expose it.
Guidance: Questions 1 and 2
- A reference identifies a Java object instance;
idrepresents domain identity in the teaching model. Two separately constructed reservations can hold the same identifier text yet remain distinct under the inheritedequals. Identifier-based equality needs an explicit contract and implementation. LoanPeriodprotects its own date relationship. Conflict prevention needs other reservations and the requested item, so it belongs at a boundary with that information.
Guidance: Questions 3 and 4
- Changed message wording affects presentation but currently requires editing the coordinator. A private helper makes the purpose easier to locate; it does not remove the class-level dependency. Zero dependencies would prevent collaboration; extra classes can add indirection without solving a problem.
- A reservation has a period. It is not a period subtype. Inheriting would conflate reservation identity and borrower/item details with an interval, and suggest inappropriate substitutability.
LoanPeriodis alsofinal.
Guidance: Questions 5 and 6
- Returning the mutable internal list allows
clearoraddoutside the coordinator’s validation.List.copyOfblocks structural mutation and isolates later additions, but does not deep-copy mutable elements. This starter’s elements are separately immutable. - A success string is returned for a reservation the coordinator does not retain. The next request can wrongly find no conflict. The
one success storedcheck exposes the fault immediately.
Transfer Challenge: Room Booking
A university room-booking coordinator stores bookings, checks room conflicts and constructs confirmation text. Its users now request a wording change only.
- Propose a responsibility diagram with at most four domain or application classes.
- State a period convention.
- Identify the state that callers must not modify directly.
- Describe one small behaviour-preserving improvement and two before-and-after observations to accept it.
- Include a brief review description and one remaining limitation.
Transfer Challenge: Guidance
- One reasonable model:
RoomBookingCoordinator,RoomBooking,BookingPeriod,BookingRules. - State an explicit convention, for example start-inclusive, end-exclusive periods in local building time.
- Extract first, change the wording second: preserve the old output while extracting; then change the text as a separate behaviour change with updated expectations.
- Two observations: identical confirmation text and identical stored records; a conflicting request still adds nothing.
- A limitation: one process’s list cannot coordinate several application instances.
Summary: Cohesion, Coupling and the Limited Claim
- Cohesion and coupling help us discuss the consequences of change.
- The formatting extraction makes one operation easier to follow without resolving every responsibility the coordinator holds.
- Explaining that limited improvement is part of sound design reasoning.
Sources and Further Reading
- Dev.java: Classes and Objects; Inheritance.
- Oracle: Java SE 17 API for
LocalDate,ObjectsandList. - Chacon and Straub: Pro Git: Recording Changes; Branching Workflows.
- Git documentation:
git diffandgit switch. - Conventional Commits 1.0.0.
- Martin, Robert C.: Agile Software Development: Principles, Patterns, and Practices, 2002.
- Full references and links are in the chapter text.