Chapter Roadmap (1 of 3)
- 9.1 What changes, and why? Variation in behaviour
- Four questions for one domain: which rule, which action, who reacts, what request.
- 9.2 Strategy: swapping one rule for another
- Already present in the borrowing policies.
- 9.3 State: behaviour that depends on where you are in a lifecycle
- A transition table first, then two implementations.
- 9.4 Observer: telling interested parties without knowing who they are
- Order, subscription changes and failure, defined before dispatching.
Chapter Roadmap (2 of 3)
- 9.5 Command: a request you can carry before running it
- A request as an object: when is it captured, and when do its effects happen?
- 9.6 Testing behaviour that patterns organise
- Let the requirement supply the expected result.
- 9.7 Choosing a pattern, and knowing its limits
- 9.8 Strategy or State? Judging AI proposals Strategy or State?
Chapter Roadmap (3 of 3)
- 9.9 Behaviour-pattern habits that mislead
- 9.10 Try it yourself: lifecycle, publisher and command
- Six steps; step 6 is on your team’s project.
- 9.11 Questions to think with, and a transfer challenge
- A document workflow: draft, approved, published.
- 9.12 The critical book review
- The critical book review is due in Week 8.
- What to take away, and what comes next
- Chapter 10: application structure and dependency direction.
Module Connection and How to Use This Chapter
- Supports LO2 and LO4. Entry: Chapters 7 and 8.
- Core reading: 9.1 to 9.3, 9.6 and 9.7 (Strategy, State, testing, selection).
- Working-knowledge reading: 9.4 and 9.5 (Observer, Command): understand the structure, ordering and failure rules and cost; see each work in the examples; you do not implement either on your own.
- Minimum lab: steps 1 to 4 and 6. Steps 1 to 4: the book’s project. **Step 6: your team’s project.
- If you want more: the AI activity (step 5), review questions and transfer challenge.
Starting Version
- Chapter 8’s Java 17 application, directory adapter and complete Maven suite.
- The existing borrowing policies already demonstrate Strategy.
- A notification publisher is an optional running collaborator.
- The proposed lifecycle is a separate exercise: it does not replace the running project’s reservation or loan rules.
- The critical book review (Week 8) is governed by the module leader’s instructions; evidence must come from the selected book, not an AI summary.
Different Reasons to Vary
- Category selects a rule.
- Lifecycle history determines which action is legal.
- Subscribers determine who reacts to an accepted reservation.
- One large conditional method could combine them. One generic “behaviour pattern” would conceal their differences.
- A fourth question: a request as an object a caller can execute later. When is it captured, and when do its side effects happen?
Four Different Questions
| Question | Primary variation | Candidate |
|---|---|---|
| Which count/duration rule applies? | Chosen algorithm or policy | Strategy |
| Which action is legal now? | Current lifecycle position and transitions | State |
| Who reacts to this occurrence? | Registered interested recipients | Observer |
| What action should this request object execute? | Captured receiver and arguments | Command |
Policy vs. Lifecycle
BorrowingPolicyanswers whether a count and duration are permitted. Changing the policy does not change what a reservation is or whether a camera is already reserved.- Lifecycle asks something else: the same cancellation may be legal for a confirmed reservation and illegal after checkout. A completed transition changes which later actions are possible.
- Selecting another duration algorithm cannot capture that history.
Observer and Command Concern Others
- Observer: interested recipients. A desk display and a local audit observer respond to the same confirmed reservation without becoming borrowing rules.
- Command: a request as an action object. It captures the receiver and arguments, then invokes the receiver when executed.
- The requirements determine which structure is useful. Before drawing classes, write one example where the behaviour varies and one outcome that must remain unchanged.
The Same Application Need Not Use All Four
- A two-branch conditional may remain clearer than an abstraction if the decision is small and stable.
- A direct method call is often sufficient if the request never needs to be stored, passed or handled independently.
- “All conditionals violate good design” is false. The question is whether decisions are duplicated, mixed with unrelated responsibilities or hard to extend safely.
- Pattern-based code still contains conditions: it relocates them.
Keep Structural and Behavioural Changes Distinct
- The State exercise compares two implementations of the same proposed table: a structural comparison in an isolated example.
- Integrating it into the lending application would introduce new lifecycle behaviour and decisions about counts, availability and loans.
- The optional publisher adds observers behind the existing notification boundary: its ordering and failure policy are new, explicit behaviour.
- Not everything is “only refactoring”.
Strategy Is Already Present
- Strategy supplies interchangeable implementations of a behaviour through a shared contract.
ReservationRules: a student count below three, one to seven days.StaffReservationRules: below five, up to fourteen days (illustrative).- The coordinator depends on
BorrowingPolicy, not on the calculations. Its category-to-policy map is supplied through construction.
What Is and Is Not Dynamic
- A strategy need not be replaced while an operation runs.
- The policy map is fixed for a coordinator instance; each request selects an entry by category.
- Configuration-time selection plus per-request lookup is enough to show the role.
- Chapter 8’s directory supplies the category at the entry point: it does not choose arbitrary policy code or rewrite duration limits.
Compare With the Conditional Starting Point
- The teaching test includes a small conditional: if student, student limits; otherwise (for the two supported enum values) staff limits.
- Understandable for two rules in one place.
- The pressure for Strategy appears when algorithms acquire distinct reasons to change or repeated callers need consistent selection.
- The conditional is a teaching example only: no duplicate policy engine in the application.
Independent Expected Results
- The comparison checks both the conditional and the policy objects against independently written boundary results.
- Testing only that the two implementations agree is weaker: both could contain the same mistaken maximum.
- Eight rows: a student at two records requesting seven days; a student already at three; staff at four requesting fourteen; negative counts; zero and excessive durations.
The Common Contract
- Every policy accepts the same two integers and returns a deterministic boolean without changing application state.
- Negative counts and non-positive durations return false.
- A true answer means the rule permits a request. It says nothing about identity, stock or interval conflicts.
- A strategy that performs directory lookup, sends a message or stores a reservation no longer behaves like this contract, even with a matching signature.
A Temporary Staff Strategy
- A test supplies a staff strategy permitting at most one counted reservation and two days. The coordinator is unchanged.
- Three days: rejected. Two days: accepted (R1). Another request for that borrower: rejected. A student’s seven-day request still succeeds under its own strategy.
- An isolated configuration test, not a change to the book’s default staff policy.
Trade-Off and Simpler Alternatives
- Strategy separates variation from the use case and makes the chosen behaviour easy to supply in a test.
- It adds a selection responsibility: someone must choose a compatible policy, and the constructor must reject an incomplete map.
- If the only difference is two numeric limits, one parameterised
LimitPolicy(maxCount, maxDays)may be simpler. If rules later differ substantially, separate strategies clarify. - Do not add a
setPolicymethod to look dynamic: it raises questions about requests in progress and reproducibility.
The Proposed Rules
- Begins at REQUESTED; may be confirmed or cancelled.
- A confirmed reservation may be checked out or cancelled.
- An on-loan reservation may be returned.
- RETURNED and CANCELLED are terminal for the four supported actions.
- Teaching rules for an isolated model. The running system confirms accepted reservations immediately, tracks loans separately, and has no cancellation operation.
What a Rejection Means
- Every rejection throws
IllegalStateExceptionand leaves the status unchanged. - Repeating cancellation is a rejection, not an idempotent success.
- That choice belongs in the table before it appears in Java.
- Twenty parameter rows cover five statuses and four actions: five permitted transitions and fifteen rejected combinations.
Reading a State Diagram
- A state diagram shows the positions an object can be in and the events that move it: nothing about implementation.
- Each rounded box is a state; an arrow is a transition labelled with its event.
- The filled circle is the start point (REQUESTED).
- A state with no outgoing arrow (RETURNED) allows none of the four actions.
- A guard is a condition in square brackets that must hold before a transition fires. This model has none.
The Absent Edges Are Specification Too
- The diagram shows permitted paths only.
- An action with no arrow from a state is not allowed there.
- The table remains the complete specification: it states the absent edges, and those are essential for testing.
- “Terminal” means no supported outgoing action. It does not mean deleting a persisted record.
The Conditional Implementation
public void cancel() {
switch (status) {
case REQUESTED, CONFIRMED -> status = Status.CANCELLED;
default -> throw rejected("cancel", status);
}
}ConditionalReservationswitches on a status enum inside four operations.- The repeated selection is easy to inspect at this size. As operations grow, one state’s complete behaviour is spread across several methods.
The State Implementation
StateReservationholds a current state object and delegates each operation to it.- Each state provides its allowed successors; default implementations reject unsupported operations.
- The context assigns the returned successor only after the state operation completes.
public void cancel() {
phase = phase.cancel();
}Reading Figure 9.2
- Both implement the same
Lifecycleinterface: a caller cannot tell them apart. ConditionalReservationkeeps a status value and switches on it.StateReservationholds aPhaseand delegates: the enum constants are the state objects.- In the code
Phaseis private; the diagram shows it because it explains the design. - The realisation arrows say both keep the same promise.
State Objects as an Enum (1 of 2)
private enum Phase {
REQUESTED(Status.REQUESTED) {
@Override Phase confirm() { return CONFIRMED; }
@Override Phase cancel() { return CANCELLED; }
},
CONFIRMED(Status.CONFIRMED) {
@Override Phase checkOut() { return ON_LOAN; }
@Override Phase cancel() { return CANCELLED; }
},- Each constant overrides only the transitions the table permits.
State Objects as an Enum (2 of 2)
ON_LOAN(Status.ON_LOAN) { @Override Phase returnItem() { return RETURNED; } },
RETURNED(Status.RETURNED),
CANCELLED(Status.CANCELLED);
Phase confirm() { throw rejected("confirm", status); }
Phase checkOut() { throw rejected("check out", status); }
Phase returnItem() { throw rejected("return", status); }
Phase cancel() { throw rejected("cancel", status); }
}- Everything not overridden is rejected by default, so the terminal states need no code.
Shared State Objects, Separate Progress
- The enum constants contain no reservation-specific mutable data.
- Two contexts can refer to the same REQUESTED constant without sharing progress: each context owns its own current-state reference.
- A test advances one context while leaving another unchanged.
- An enum used only as a tag in a central switch would be the conditional design instead.
No Public Setter
- No caller can assign a new phase directly.
- A public setter would let a caller skip confirmation or resurrect a returned reservation without passing the rules.
- Read-only status is sufficient for observation.
- That the State example has no public setter is established by reading the API, not by a runtime test.
Test, Trade-Off and Integration Boundary
- Twenty parameter rows, each run against both implementations, with an independently specified outcome; rejected actions preserve state.
- Comprehensive for the small table, not for a real reservation process: time windows, permissions, conflicts, cancellation effects and loan storage are outside it.
- State groups behaviour by lifecycle position but adds indirection. For this small fixed table, keeping the conditional is defensible.
- Before integrating: do cancelled reservations stop counting? Do intervals become available? These are requirements, not consequences of State.
Problem and Simple Attempt
- After acceptance, several local components may react.
- Hard-coding each observer in the coordinator couples reservation decisions to the changing recipient list.
- A direct call remains simpler for a single fixed collaborator: the existing sender interface already supports that.
The Optional NotificationPublisher
- It implements
NotificationSender, so the coordinator uses it through its existing boundary. - Observers register a callback receiving the immutable confirmed reservation.
- Registration and dispatch are the publisher’s responsibility, not new methods on the coordinator.
- Java’s
PropertyChangeSupportis a listener-management example. This publisher has its own documented contract: it does not inherit another library’s ordering, concurrency or failure guarantees.
The Publisher’s Contract (1 of 2)
| Situation | Selected outcome |
|---|---|
| No observers | Normal return; no delivery claim |
| Same observer object registered twice | Reject duplicate registration |
| Subscription closed | Remove that registration; repeated close is harmless |
| Subscribe or unsubscribe during a callback | Change subsequent publications, not the current snapshot |
The Publisher’s Contract (2 of 2)
| Situation | Selected outcome |
|---|---|
Expected NotificationException | Retain failure and continue to later observers |
| Unexpected exception | Propagate immediately; later observers are not attempted |
| Recursive publication through the same publisher | Reject with IllegalStateException |
- Observer identity uses the same object reference, not custom equality. The publisher does not deduplicate recipients by business identity.
The Publisher’s Core
for (Entry entry : List.copyOf(entries)) { // snapshot
try {
entry.observer.onConfirmed(reservation);
} catch (NotificationException failure) { // expected failure
if (aggregate == null)
aggregate = new NotificationException("One or more observers failed");
aggregate.addSuppressed(failure);
}
}
if (aggregate != null) throw aggregate;- A
finallyblock resets the recursion guard.
Subscription Handles Own One Registration
- A handle owns one registration. Closing an old handle again must not remove a later registration of the same observer.
- The implementation keeps a distinct entry object per registration to make that ownership precise.
- An implementation removing by observer identity on every close could incorrectly remove a newer registration.
Publication Occurs After Storage
- The coordinator stores the accepted reservation before calling its sender.
- If an observer fails with the expected exception, the publisher continues, collects the failures, and throws one aggregate
NotificationException. - The coordinator returns its existing suffix: notification failed; reservation retained.
Reading Figure 9.3
- Partial success: the desk and audit observers ran; the display observer failed.
- It does not roll back earlier observer effects, and the suffix does not mean every observer failed.
- The aggregate keeps individual exceptions as suppressed exceptions.
- The coordinator returns only its established message: no detailed observer diagnostics.
Unexpected Failures
- An unexpected programming exception stops dispatch and escapes the coordinator, which catches only
NotificationException. - The reservation still exists because storage happened earlier.
- A caller must not infer “nothing happened” from the fact that the operation threw.
- The recursion guard is not a concurrency lock or a rollback system. Callbacks must not mutate the coordinator or start new reservations.
The Snapshot Rule in Action
first, second, first, third- During the first publication, the first observer removes the second and adds a third.
- The snapshot already includes the second, so it still runs. The next publication sees the updated registrations.
- A declared contract, not an accident of
ArrayListiteration. Deterministic sequence tests, not concurrent-race tests.
Trade-Off and Non-Use Case
- Observer reduces direct knowledge of recipients but makes execution less obvious: work happens inside registered callbacks.
- Ordering dependencies can become hidden. If one observer must finish a business step before another begins, explicit orchestration may express it better.
- Keep a single sender for a fixed output.
- No durable delivery, broker, asynchronous execution or exactly-once guarantee. Not an automatic next step.
Problem
DirectoryReservationService.reserveimmediately performs lookup and reservation.- A user interface action or a small invoker may need an object containing the request, to execute when selected.
Command<R>has one method,R execute().ReserveEquipmentCommandcaptures the service, borrower id, item id, start date and days.
The Command Record
public record ReserveEquipmentCommand(DirectoryReservationService receiver,
String borrowerId, String itemId, LocalDate start, int days) implements Command<String> {
public ReserveEquipmentCommand {
Objects.requireNonNull(receiver, "receiver");
if (borrowerId == null || borrowerId.isBlank() || itemId == null || itemId.isBlank()) {
throw new IllegalArgumentException("Identifiers must not be blank.");
}
Objects.requireNonNull(start, "start");
}
@Override public String execute() {
return receiver.reserve(borrowerId, itemId, start, days);
}
}What Construction Does Not Do
- Construction validates required references and identifiers.
- It performs no lookup, allocates no reservation, notifies no one.
- The receiver stays responsible for the use case: the command does not reproduce borrowing policy or store its own reservation list.
- A lambda or method reference can be enough if no named request data is needed.
Runnableis a familiar no-result action, but using it is not a Command architecture.
Time of Execution Changes the Outcome
- Between construction and execution, another request may reserve the item.
- The command must evaluate current receiver state when executed. Capturing an earlier “available” result and using it as permission bypasses the actual conflict check.
- The record’s fields are immutable, but it holds a reference to a mutable service. It is not a snapshot of application state.
- A test constructs the command, creates a competing reservation, and verifies rejection when it later executes.
Failure, Undo and Alternatives
- A directory failure propagates unchanged: the command does not convert it into success, retry it or compensate.
- Undo needs its own semantics. Deleting a record would not undo an observer’s side effect; the project has no cancellation contract.
- Naming a class
Commandsupplies no undo, transactions, queueing or persistence. - Use a direct call when there is no reason to represent the action separately.
What Each Pattern’s Tests Need
- A test that checks an object implements
BorrowingPolicycannot establish correct limits. - A test that constructs a state object cannot establish a valid transition.
- A test that counts subscribers cannot establish what happens after one throws.
- Strategy: independent boundary examples. State: the transition table, including rejected edges. Observer: ordered event histories and state after failures. Command: distinguish construction from execution.
Compare Without Circular Proof
- The lifecycle tests send the same action to the conditional and State implementations, but both are checked against a fixed table.
- Equality between the two alone is insufficient: if both allowed cancellation on loan, they would agree while violating the requirement.
- The table covers this exercise’s complete state/action space. It does not prove arbitrary sequences with time, external state or permissions. Keep the claim proportional to the model.
Test Partial Effects Explicitly
- Unexpected exception: earlier callbacks ran, later ones did not, the coordinator retained its record. More informative than checking only the thrown exception.
- Expected failure: later observers still run, and the aggregate keeps both failures in order.
- Subscription changes: removal during a callback affects the next publication; the removed observer still appears in the current snapshot.
Evidence Narrows Uncertainty
- The command replay test documents one concrete repeated execution. It guarantees nothing about future commands or other receiver states.
- Passing evidence should narrow uncertainty about the selected promise, not expand into unsupported general assurance.
- The full build executes 107 JUnit invocations: 63 inherited and 44 new.
Reasonable First Options
| Situation | First option | Pattern when justified |
|---|---|---|
| Two small fixed limit rules | One conditional or parameterised policy | Strategy for independent implementations |
| A small fixed transition table | Enum and explicit operations | State when grouping state-specific behaviour clarifies |
| One fixed notification output | Direct injected sender | Observer for a changing set of local recipients |
| One immediate reservation request | Direct service call | Command when the request must be represented separately |
No Row Says More Classes Is Progress
- A useful explanation identifies where a future edit will occur, which contract stays stable and which new obligations arise.
- Observer centralises registration but needs decisions about order and failure.
- State centralises each state’s actions but needs consistency between model and tests.
Be Precise About This Version
- The project keeps Chapter 8’s directory boundary and all previous application sources.
NotificationPublisheris an additional sender option; no earlier demo is silently rewired.- The State model stays in the separate
behaviourpackage and never updates real reservation or loan storage. - Inherited limits remain: in-memory storage, trusted internal APIs, no enforced identity, and reservation records that still count and block intervals after loan return.
A Design Decision Must Be Challengeable
Several local observers need to react; a publisher owns registration and dispatch; one fixed sender remains simpler where recipients do not vary; failures can leave partial effects.
- “Observer decouples everything” cannot be assessed.
- “State eliminates invalid operations” is too broad unless the API and rules enforce the model.
Ground Rules for the Activity (1 of 2)
| For this activity | |
|---|---|
| Your goal | Decide whether each proposal fits a policy-selection problem or a lifecycle problem, and what each allows a caller to do |
| You start with | The lifecycle table in 9.3, the existing strategies, and the failure and replay behaviour in 9.4 and 9.5 |
| What is in bounds | Compare the proposals and qualify the reliability claim; adopt none without the table |
Two Fictional Proposals
Proposal A: “Implement RequestedPolicy, ConfirmedPolicy and ReturnedPolicy as strategies. Expose setPolicy so callers can choose any policy before each operation. Calling a return method should simply select the returned policy.”
Proposal B: “Store the current phase inside a reservation context. Delegate confirm, checkout, return and cancel to phase-specific behaviour. Allow only transitions from the reviewed table and preserve the current phase after a rejected action.”
Verdicts
- Proposal A exposes policy selection with no transition authority: a caller can pick the returned policy though no checkout occurred. Calling the classes strategies does not solve legal histories.
- Proposal B puts the phase in the context and restricts changes to reviewed actions: the stronger match, provided the actual implementation rejects invalid transitions and preserves state. More appropriate wording is not evidence.
- For student/staff duration rules the pressure differs: they are selected policies, not stages.
A Third Claim
Observer guarantees that all recipients receive the event once, and Command makes retries safe.
- Observer does not guarantee every callback completes: the expected-failure policy continues but still reports partial success; an unexpected failure stops later callbacks.
- Command does not make replay safe: repeated execution re-enters the receiver; the conflict check here is not a general deduplication mechanism.
- Undo and compensation need separate agreed semantics.
Evidence to Retain
- For each AI claim: the requirement, the claimed structure, the confirming or rejecting test, the remaining limitation.
- Example entry: “Model claims cancellation is legal from any state; transition table forbids ON_LOAN/CANCEL; test requires an exception and unchanged ON_LOAN; proposal rejected.”
- Do not use generated implementation and generated expectations as mutual proof.
Mistakes 1 to 3
| Mistake | Consequence | Better action |
|---|---|---|
| Treat category policies as lifecycle positions | Confuses selection with permitted transitions | Identify what changes the behaviour |
| Expose an unrestricted state setter | Allows invalid jumps | Change state only through reviewed operations |
| Store context data in shared state objects | One reservation may affect another | Keep progress in each context |
Mistakes 4 to 6
| Mistake | Consequence | Better action |
|---|---|---|
| Catch all observer errors as expected failures | Hides programming defects | Specify and catch the declared failure type |
| Assume a thrown notification means no reservation exists | Misreads partial effects | Check the storage-before-publication sequence |
| Remove observers from a live iteration | Makes dispatch fragile | Define and test snapshot semantics |
What You Start With, and the Demonstration
- You start with: the Chapter 8 version, the lifecycle table, the conditional and State examples, the publisher contract. Steps 1 to 5: the book’s project. **Step 6: your team’s project.
- Demonstration: locate the existing strategies; trace permitted and rejected lifecycle actions before showing the two implementations. Run one expected observer failure and show the retained reservation and the later callback.
- Guided practice: choose a table row and explain its result independently of the code. Predict the effect of unsubscribing during dispatch. Compare a direct service call with constructing and executing a command.
Independent Work: Steps 1 to 3
- Replace the repeated conditional in the isolated lifecycle with State delegation, preserving the table.
- Show permitted and rejected actions, including unchanged status on failure and independent contexts.
- Demonstrate the publisher’s order and expected-failure continuation. Construct a
ReserveEquipmentCommandwithout executing it, show construction performs no reservation, then execute it.
Independent Work: Steps 4 to 6
- Choose a second scenario and justify adopting or declining Strategy, Observer or Command against a simpler implementation.
- Evaluate the fictional AI proposals and retain independent evidence.
- On your team’s project, find one conditional that selects behaviour by type or tracks a lifecycle. Write a test that protects its current behaviour, then decide whether Strategy, State or neither is justified. Record what you would decline to build and why.
Expected Output and Acceptance
- What you should end up with: before and after source; a reviewed transition table; runnable tests; a reproducible demo; a concise problem-solution-consequence note; the step 6 decision.
- Accept when: no unrestricted phase setter; correct terminal behaviour; outcomes match independently stated expectations; observer failure does not imply rollback; command construction performs no reservation; the old regression suite still runs.
- If you want more: propose real cancellation. Specify effects on counts, availability, active loans, identifiers and notifications before changing code.
A Suggested State Refactoring Sequence
- Begin with the complete table and
ConditionalReservation; run the table tests. - Introduce private State objects implementing the four operations: reject by default, override only permitted transitions.
- The context delegates and accepts a returned successor only on success.
- Run the same table against both; add the independent-context check.
- Inspect the public API for a setter.
- A reasoned conclusion may still favour the conditional for this small model.
A Second Decision: Adopt Observer Selectively
- Problem: several local components react after acceptance, and their registrations can change.
- Solution: an optional publisher implementing the existing sender interface; ordered snapshots; the declared failure rule; the demo supplies it explicitly.
- Consequences: the coordinator stays independent of observer identities; partial effects; observers need lifetime management; unexpected failures escape after storage.
- Simpler alternative: one injected sender; or explicit orchestration when several steps form one business operation.
Decline a Pattern With a Concrete Reason
- A button handler can call the service if it never needs to carry the request separately: Command adds a type with no demonstrated benefit.
- One count/duration formula parameterised by two limits may be enough: separate Strategy classes pay off when rules deserve independent implementations.
- Name which change each alternative makes easier and which obligation it adds.
Concept Questions
- Why is category-based borrowing policy a Strategy example while the proposed reservation lifecycle is a State example?
- Why can stateless enum-based State objects be shared while reservation progress must remain separate?
- What does snapshot iteration mean for an observer removed during publication?
- What does a Command capture, and which decisions still belong to execution time?
Diagnosis Questions
- An unexpected observer exception occurs after two callbacks succeed. The caller assumes no reservation was stored and immediately repeats the request. Explain why that assumption is unsupported and identify the observations needed before choosing a recovery action.
- An AI-generated State implementation has
setStatus(RETURNED)and a happy-path test. Explain the missing guarantee and design tests that reject an invalid jump or action sequence.
Guidance: Questions 1 and 2
- Category selects which count/duration policy applies: it does not advance a lifecycle. State makes the meaning or legality of an operation depend on the context’s current position. Similar delegation structures can serve different purposes.
- The constants hold only behaviour and a fixed status. Each context owns its current reference. Mutable reservation data in a shared constant would couple contexts. The independence test advances one context and observes the other still at REQUESTED.
Guidance: Questions 3 and 4
- An observer present when publication begins still receives that publication even if a prior callback closes its subscription; it is absent from the next snapshot.
- The receiver and request arguments, not a snapshot of receiver state or an advance permission. Lookup, eligibility and conflicts are evaluated when
executecalls the service.
Guidance: Questions 5 and 6
- The coordinator stores before publishing, so the exception is compatible with a stored reservation and partial external effects. Inspect the reservation snapshot and the known event history before recovery; do not blindly repeat.
- A setter bypasses the transition table. Test: checkout before confirmation, cancellation while on loan, repeated return: each must reject and preserve status. Add valid sequences and independent contexts so the code cannot pass by rejecting everything.
Transfer Challenge: A Document Workflow
A document workflow has draft, approved and published stages. Review policy differs by department, several local views react after publication, and a user interface can hold a publish request before executing it.
- Identify the four variation pressures; choose only the justified patterns.
- Specify one invalid transition, one observer failure and one stale-command outcome.
- State which requirements need clarification before implementation.
Transfer Guidance
- A department review algorithm: Strategy candidate. Draft, approved, published: State candidate. Local views reacting: Observer. A UI-held publish request: Command, if it needs separate representation.
- Do not require all four.
- Invalid transition: publishing a draft before approval; it must stay a draft.
- Observer failure: a preview fails after publication was stored: do remaining observers run, and what does the caller see?
- Stale command: a publish captured while approved, executed after an edit returned it to draft: the receiver must check current state and reject.
What the Version Says
- Your individual critical review of a book from the approved list is due in Week 8.
- Appendix E develops the method: a reading record, questions for each part, prompts for checking sources.
- The current assignment brief is always the authority on length, sources, format and deadline.
- Read the selected book directly, not a summary. Keep apart what the author claims, what you infer and what an AI tool suggested.
For a Pattern Discussion
- Does the book’s example establish the extension benefit it claims?
- Which simpler alternative did the author consider?
- What cost does the author acknowledge?
- A criticism resting on a source and a concrete case is stronger than a catalogue of definitions.
- Reading this textbook does not satisfy the assignment, and an AI summary of an unread book is not evidence about it.
Sources and Further Reading
- Gamma, Helm, Johnson, Vlissides: Design Patterns: the Strategy, State, Observer and Command entries.
- Oracle: Java SE 17
PropertyChangeSupport,ThrowableandRunnable. - JUnit Team: JUnit 5.13.4 User Guide.
- Martin: Agile Software Development (2002): the Strategy, Command, Observer and State chapters.
- Full references and links are in the chapter text.