Design Patterns II: Strategy to Observer
The second half of the GoF patterns: Strategy, Composite, Observer and Template Method in depth, three more from the textbook in brief, and then the skills the exam actually rewards. You will learn to tell look-alike patterns apart, recognise a pattern from a one-paragraph scenario, and write the model answers to every past pattern question.
- You willdraw Strategy, Composite, Observer and Template Method from memory, with every role in the right place.
- You willtrace each pattern at run time: delegation, recursion over a tree, and notification fan-out.
- You willseparate confusable patterns (Strategy, State and Template Method; Adapter, Façade, Decorator and Proxy) and pick the right one from a scenario.
- You willanswer "describe this class", "list the patterns" and "draw a Composite DCD" questions the way the official answers do.
Strategy exam
This Part continues the catalogue of Gang of Four (GoF) patterns that Part VI began with Façade, Decorator, Builder, Adapter, Factory and Singleton. Every pattern here is drawn with the same colour code, so before we start, here is what the colours mean. Once you know it, you can glance at any diagram on the page and see at once which box plays which part.
Strategy solves a very common problem: the same thing can be calculated in several different ways, and the choice of way changes. A shop prices a sale differently for seniors, on Mondays, or during a promotion. If all those rules live inside one class as a big if/switch, that class has to be edited every time a rule changes. Strategy pulls each rule out into its own small class.
Intent: define a family of algorithms, encapsulate each one, and make them interchangeable. The algorithm can then vary independently of the clients that use it.
Problem (Larman): "How to design for varying, but related, algorithms or policies? How to design for the ability to change these algorithms or policies?"
Solution (Larman): "Define each algorithm/policy/strategy in a separate class, with a common interface."
How to spot it in a scenario
Strategy is a behavioural pattern: it is about how responsibility for an algorithm is shared between objects. The give-away in an exam stem is a list of different ways to do the same job: to calculate, price, discount, tax, score, analyse, assign, route or sort. Another tell is a policy that depends on something like membership level, customer type, time of day, day of the week or country. Look for these phrases too:
- "the rules change frequently";
- "a large conditional that switches between variants of the same algorithm" (workshop 9);
- a need to "swap the behaviour at run time";
- a plan to "plug in new algorithms later, such as an ML engine".
The two tabs below show the same pricing problem first as a switch inside Sale, then as Larman's Strategy design.
Sale.getTotal(). A new rule, or a change to an old one, means editing and re-testing Sale, and the rule cannot be swapped while the system runs. This "large conditional that switches between variants of the same algorithm" is the classic Strategy symptom.pricingStrategy, typed to the violet ISalePricingStrategy interface. The two green ConcreteStrategies realise it (dashed line, hollow triangle). Notice that getTotal takes the Sale as a parameter: that is how a strategy reads the data it needs. A new pricing rule is a new green class; Sale never changes.Participants and structure
Strategy has three core roles, plus a factory that usually travels with it.
- The Strategy is an interface declaring the algorithm's operation.
- Each ConcreteStrategy implements one version of the algorithm.
- The Context holds an attribute typed to the interface and delegates to it. It usually passes itself (
this) as an argument, so that the strategy has parameter visibility back to the context; Larman says this is "usually required". - Strategies are usually created by a Factory, often a Singleton. Workshop 9 puts it this way: Strategy "is often used in combination with the Factory pattern to determine which algorithm to use."
this. ConcreteStrategies are interchangeable; a Factory (often a Singleton) decides which one to create. Point the association at the interface, never at a concrete strategy.The textbook example in code
Here is Larman's pricing design in Java. Compare each line with the "With the pattern" diagram above: the interface, the two realising classes, and the Sale holding one field of the interface type.
public interface ISalePricingStrategy { double getTotal(Sale s); }
public class PercentDiscountPricingStrategy implements ISalePricingStrategy {
private double percentage = 0.05;
public double getTotal(Sale s) { return s.getPreDiscountTotal() * (1 - percentage); }
}
public class AbsoluteDiscountOverThresholdPricingStrategy implements ISalePricingStrategy {
private double threshold = 50, discount = 5;
public double getTotal(Sale s) {
double pdt = s.getPreDiscountTotal();
return pdt >= threshold ? pdt - discount : pdt;
}
}
public class Sale { // Context
private ISalePricingStrategy pricingStrategy =
PricingStrategyFactory.getInstance().getSalePricingStrategy(); // created by a Factory
public double getTotal() { return pricingStrategy.getTotal(this); } // delegate, passing this
public void setPricingStrategy(ISalePricingStrategy ps) { pricingStrategy = ps; } // swap at run time
}
Three lines in Sale carry the whole pattern. The field is declared with the interface type. getTotal() simply delegates, passing this. And setPricingStrategy lets the rule be swapped while the program runs, something a switch on a type code cannot do without editing the class.
- The run-time picture starts with three objects: the context s : Sale, its line items, and the strategy object that the Sale's
pricingStrategyfield currently points to. - Someone (a Register, say) asks the Sale for its total: t := getTotal(). The Sale does not know any pricing rule itself.
- First the Sale works out its pre-discount total the ordinary way, by asking each line item for its subtotal in a
loop. - Delegation. The Sale hands the pricing decision to its strategy:
getTotal(s). It passes itself as the argument, which Larman says is "usually required", so the strategy gets parameter visibility back to the context. - Callback. Using that parameter, the strategy asks the Sale for
getPreDiscountTotal(). This is the price of the design: the strategy is coupled to the context's interface. - The strategy applies its own rule (here a percentage discount) and returns
t, which the Sale passes back. Plug inAbsoluteDiscountOverThresholdPricingStrategyinstead and this diagram is unchanged except for the last lifeline's class.
Where the strategy comes from: the factory
Somebody still has to decide which strategy a Sale gets. In Larman's POS (section 26.7) that job belongs to a Singleton PricingStrategyFactory, with operations getSalePricingStrategy() and getSeniorPricingStrategy(). It reads the name of the strategy class from a property and instantiates it reflectively, so the pricing policy can change while the system is running. For the same reason it deliberately does not cache the strategy it creates. It also reads the percentage values from an external store, so the numbers can change without a code change.
The course's other Strategy examples
The course uses Strategy in three main examples, and it has come up repeatedly in past exams. Larman's pricing is the first. The second is Tony's Pizza delivery assignment (week 8), which decides who or what delivers an order. The Strategy interface is IAssignmentStrategy.assignResource(order: Order): IDeliveryResource. The Order is the context object passed in: it knows its priority, its number of line items, and its restaurant, which leads to the delivery fleet. Both concrete strategies build DroneComposites when they need a team of drones (you will meet that in 7.2).
assignee from the result. The Order is the context object passed in: it carries everything a strategy needs to decide. The private helpers show that each concrete strategy can be as complex as it likes inside. A future machine-learning assigner is one more green class (PV).The third example, Monopoly Express (workshop 9), uses three interchangeable scoring rules and combines Strategy with Composite. It is worked in full, with the official partial solution, in 7.3.
Strategy appeared in 2021 as membership-level analytics (S1 Q1.6, an MCQ), tax calculation (S2 Q3, "describe this Java code"), the calculator's IBinaryOp/IUnaryOp (S1 Q7) and the music lineup's IDuetMusic/ISoloistMusic (S2 Q7). The model answers are in 7.9 and 7.10. When you justify Strategy, name its GRASP roots (below) and map every class to a role: interface, concrete strategies, context.
Why it works: the GRASP behind it
The mechanism
Each concrete strategy answers the same message its own way, so the context never tests a type.The payoff
The context is protected from changes to the algorithms: a new rule is a new class.The strategies
A "pricing strategy" is not a domain concept; it is invented to hold one algorithm.The side effects
The context knows only the interface, and each class holds exactly one algorithm.Pitfalls and trade-offs
- The selection logic does not disappear. It moves into the factory or the client. Strategy removes the
switchfrom the context, not from the system. - There are more objects to create and understand.
- The strategy often needs data from the context, and passing
thiscouples the strategy to the context. Larman's idiom is to pass the aggregate object (the whole Sale) rather than pieces of it. - A uniform interface may hand some strategies data they don't need.
- Don't apply it to two trivial variants that will never change. That is "pattern-itis": the design gets heavier with nothing gained.
Check yourselfA student's design has Sale with an association to PercentDiscountPricingStrategy, and a switch in Sale.getTotal() that chooses between that class and AbsoluteDiscountOverThresholdPricingStrategy. Name the two mistakes.
First, the association points at a concrete class instead of the ISalePricingStrategy interface, so Sale cannot hold any other strategy. Second, choosing the strategy with a switch inside the context defeats the purpose: adding a policy still means editing Sale. The fix is one field typed to the interface, filled in by a factory (or a setter), and a getTotal() that just delegates. Both are on the markers' list of Strategy errors (7.12).
Check yourselfA car-park app charges by the hour on weekdays, a flat fee on weekends, and nothing on public holidays. The council changes these rules every year. Which pattern fits, and which class plays each role?
Strategy. The cue is several ways to calculate the same thing (a parking charge), chosen by day type, with rules that change often. A possible mapping: IParkingFeeStrategy with getFee(stay) is the Strategy interface; HourlyFee, FlatFee and FreeFee are ConcreteStrategies; the ParkingStay (or ticket) is the Context, holding a field of the interface type and delegating getFee(this); a Singleton FeeStrategyFactory picks the strategy from the date. It gives PV: next year's rule is a new class.
Check yourselfWhy does Sale.getTotal() call pricingStrategy.getTotal(this) instead of pricingStrategy.getTotal()?
The strategy needs data held by the context (here the pre-discount total). Passing this gives the strategy parameter visibility to the Sale, so it can call back s.getPreDiscountTotal(). Larman says passing the context is "usually required", and recommends passing the whole aggregate (the Sale) rather than individual values. The cost is that every strategy now depends on the Sale's interface.
- Strategy = one class per algorithm behind a common interface; the Context holds a field of the interface type and delegates, usually passing
this. - A Factory (often a Singleton) chooses the concrete strategy; the selection logic moves there, it does not vanish.
- Built on Polymorphism; gives PV against changing algorithms, Low Coupling and High Cohesion. Course examples: POS pricing, Tony's Pizza assignment, Monopoly Express scoring.
Composite exam
Many things in software are made of smaller things of the same kind: a picture is made of shapes and other pictures, a fleet of vessels and smaller fleets, an expression of sub-expressions. Code that works on such structures usually has to ask "is this one thing or a group?" at every level. Composite removes that question by giving the group and the single item the same interface.
Intent: compose objects into tree structures to represent part–whole hierarchies. Clients can then treat individual objects and compositions uniformly.
Problem (Larman): "How to treat a group or composition structure of objects the same way (polymorphically) as a non-composite (atomic) object?"
Solution (Larman): "Define classes for composite and atomic objects so that they implement the same interface."
How to spot it in a scenario
Composite is a structural pattern. The clearest sign is a part–whole, tree, recursive or nested structure, and the course is full of them: a house containing rooms that contain units, a building with people and the objects they carry, a picture made of shapes, a fleet of vessels, a quadtree divided into quadrants, an expression built from sub-expressions, a team of drones, an army of characters, and a menu with submenus. The wording of a scenario also gives it away. Watch for:
- a requirement to "apply a property or operation to the whole, to some parts, or to one item, in the same way";
- "the client shouldn't care whether it has one or many";
- a need to "aggregate a result over the parts" (a total, count or minimum);
- "several conflicting policies combined so they look like one".
Composite "ONLY makes sense when the core model can be represented as a tree". If there is no part–whole nesting, a group of objects is just a collection, and the pattern adds nothing.
draw(). Picture holds its children as Shapes, so a picture can contain circles, other pictures, or pictures of pictures, and the recursion happens by itself. Shape's name is in italics because it is abstract.Participants and structure
The pattern has four roles.
- The Component is an interface or abstract class. It declares
operation(), and possibly the child-management operations. - A Leaf has no children and does the real work. There can be several leaf types.
- The Composite holds 0..* children typed as Component. Its
operation()iterates over the children and combines their results, and it providesadd,removeandgetChild. - The Client uses only Component.
Larman sums up the signature feature: "the outer composite object contains a list of inner objects, and both the outer and inner objects implement the same interface."
add, remove, getChild) sits only in Composite: the "safe" form the course uses. Markers check the gold link first.The part markers look for is the recursive link from Composite to Component, with multiplicity 0..*. The common mistakes are to link it to Leaf instead, or to forget that Composite also is a Component (the triangle).
The lecture example in code
In the lecture's shapes example, Picture is the composite. It draws itself by delegating to its children, and it neither knows nor cares whether a child is a circle or another picture.
public abstract class Shape { public abstract void draw(); }
public class Circle extends Shape { public void draw() { /* draw circle */ } } // Leaf
public class Picture extends Shape { // Composite
private final List<Shape> shapes = new ArrayList<>();
public void addShape(Shape s) { shapes.add(s); }
public void draw() { for (Shape s : shapes) s.draw(); } // delegate to the children
}
What happens at run time: recursion
The class diagram is static; the interesting part of Composite happens when the operation runs. A call on the root fans out to the children, a composite child repeats the same call one level down, and the answers are combined on the way back up. The stepper traces this on the quadtree from the 2021 exam (Q9, 7.11): Black.countBlack() returns size * size, White and Outside return 0, and Divided.countBlack() returns the sum of its four children.
- The image on the left is stored as the object tree on the right. Each square region is either uniformly black, white or outside the image (a leaf), or it is Divided into exactly four sub-quadrants (a composite). The client asks the root for
countBlack()and does not care which kind it has. - The root is a Divided, so it asks its first child. ne is a Black leaf: it returns
size * size = 4 * 4 = 16without asking anyone. - se is a White leaf: it returns 0.
- sw is itself a Divided, so the same method runs again one level down: it asks its own four children. Its ne is Black with size 2, so it returns 4.
- sw's other children return 0 (White), 4 (Black) and 0 (Outside), so sw returns 4 + 0 + 4 + 0 = 8 to the root. The recursion has unwound one level.
- nw is an Outside leaf: it returns 0.
- The root adds its children's answers: 16 + 0 + 8 + 0 = 24 black pixels. One call on the root walked the whole tree, and no object ever tested the type of another. This is exactly the past-exam question in 7.11.
Safety versus transparency exam
The main design decision is where to declare the child-management operations, add and remove. There are two choices, and each gives something up.
- Transparency means declaring them in Component. All nodes then look the same, but Leaf must implement meaningless operations, either by throwing
UnsupportedOperationExceptionor by ignoring the call. - Safety means declaring them only in Composite. Misuse is then caught at compile time, but a client must know it has a Composite before it can add children.
add and remove go? Transparency puts them in the Component so every node looks alike, at the cost of a Leaf with operations that make no sense (in red). Safety puts them only in the Composite. The lecture's structure and every course example choose safety; so should your exam diagrams unless told otherwise.Other design decisions and pitfalls
- An abstract composite superclass can hold the child list, so that subclasses differ only in how they combine the results (min or max, for example). You will see this in Larman's pricing design in 7.3.
- Parent references are optional.
- You must prevent cycles: a picture that contains itself would recurse for ever.
- Restricting what can be a child (for example, "a fleet contains only vessels") is hard when children are typed as Component, so use narrower types or run-time checks.
- Recomputing aggregates over large trees can be slow, so cache the values if needed.
- The design can become overly general.
More course examples
The course uses Composite in several more places. In the lecture's Disney Army, Army implements IDisneyAdapter and aggregates 0..* IDisneyAdapter, so army.move() moves every member (the character adapters are from Part VI). Tony's Pizza (week 8) has a DroneComposite that implements IDeliveryResource alongside DeliveryStaff and single drones. Monopoly Express's CompositeRule and Larman's composite pricing strategies are covered in 7.3.
DroneAdapter, because a team only ever contains drones; its aggregation is hollow because the drones belong to the DeliveryFleet and the team exists for one order only. addDrone() builds the team at run time, and both operations just loop over the drones.GRASP. Composite is based on Polymorphism. It gives PV for the client, which is unaffected by whether it holds an atomic or a composite object. It is often used together with Strategy (next section), and with Command (a macro command is a Composite of commands, 7.6).
Q9 in both 2021 papers asked for a Composite design class diagram (the quadtree and the fleet, 7.11), and Q7.2 asked you to find Composite in given code (the calculator and the music lineup, 7.10). In MCQs, the official answer to S2 Q1.9 (a service package assembled from selectable services) is Composite. Always name the three roles, draw the recursive link to the Component, and put add in the Composite only.
Check yourselfSpot the error: a student draws Folder and File both extending FileSystemItem, with an association Folder → File labelled contents 0..*. What is wrong, and what can this design not represent?
The recursive link points at the Leaf (File) instead of the Component (FileSystemItem). As drawn, a folder can hold only files, so folders inside folders are impossible and the structure is no longer a tree of arbitrary depth. Redraw the association as Folder ◇→ FileSystemItem, role contents, multiplicity 0..*.
Check yourselfAn online course is made of modules; a module contains lessons and sometimes sub-modules; a lesson has a duration. The app must show the total duration of a lesson, a module or the whole course. Name the pattern and its roles.
Composite. The cues are a nested part–whole structure and "aggregate a result over the parts" for the whole, a part or one item alike. Component: CourseElement (abstract or interface) with getDuration(). Leaf: Lesson, returning its own duration. Composite: Module (and the course itself), holding 0..* CourseElement children and returning the sum of their durations; add() goes only in Module (safe form).
Check yourselfIn the transparent form of Composite, what must Leaf.add() do, and why does the course prefer the safe form?
A leaf cannot have children, so add() must either throw UnsupportedOperationException or silently ignore the call. Both are meaningless operations forced on the leaf. The safe form declares add/remove only in Composite, so adding a child to a leaf is a compile-time error; the cost is that a client must know it holds a Composite before adding children. The lecture and every course example use the safe form.
- Composite: Leaf and Composite share the Component interface; the Composite holds 0..* Components and its operation loops over them and combines the results.
- The mark-earning detail is the recursive link from Composite back to Component, plus Composite also being a Component.
- Prefer the safe form (add/remove only in Composite). Based on Polymorphism; gives PV because the client cannot tell one from many.
Strategy and Composite Together: Composite Strategies
Strategy lets a context hold one interchangeable policy. But what if several policies apply at once and must be reconciled? Composite answers that: make a "group of strategies" that is itself a strategy. The context still holds one object, and has no idea that behind it sits a whole set of rules. The course uses this combination twice, in Larman's pricing and in Monopoly Express, and it reappears in the 2021 exam code (7.10).
The problem: several pricing policies at once
A store may have several policies in force on the same day:
- a 20% senior discount;
- 15% off for preferred customers on sales over $400;
- $50 off purchases over $500 on Mondays;
- 15% off everything if you buy a case of Darjeeling tea.
Different policies are triggered by different things: the customer type, the time period, or the product on a line item. When several apply, a conflict-resolution policy decides the outcome, either best for the customer (the lowest price) or best for the store. Throughout, the Sale should not know whether it holds one strategy or many, which is exactly what Composite gives it.
strategies list (the recursive link, back to the interface), and its two subclasses differ only in how they resolve conflicts: lowest price or highest.public abstract class CompositePricingStrategy implements ISalePricingStrategy { // abstract Composite
protected List<ISalePricingStrategy> strategies = new ArrayList<>();
public void add(ISalePricingStrategy s) { strategies.add(s); }
public abstract double getTotal(Sale sale);
}
public class CompositeBestForCustomerPricingStrategy extends CompositePricingStrategy {
public double getTotal(Sale sale) {
double lowest = sale.getPreDiscountTotal();
for (ISalePricingStrategy s : strategies) lowest = Math.min(lowest, s.getTotal(sale));
return lowest; // CompositeBestForStore would use max
}
}
Notice the design decision from 7.2 at work: the abstract composite superclass holds the child list and add(), so its subclasses differ only in how they combine the children's answers (Math.min here, max for the store).
- A senior customer buys $600 of goods on a Monday, so two of the store's policies apply (both are from Larman's list). The Sale holds a single composite strategy, which holds the two leaf strategies.
- The Sale calls getTotal(s) exactly as it would on any strategy. Following the Java below, the composite starts with
lowest= the pre-discount total, 600. - The composite asks its first child. The senior discount gives 20% off: 480. The minimum so far is 480.
- The Monday discount takes $50 off purchases over $500: 550. That is higher than 480, so the minimum stays 480.
- The composite returns the best price for the customer, 480. The Sale never knew that two policies were consulted; a
CompositeBestForStorePricingStrategywould have returned the maximum instead.
strategies[j] : ISalePricingStrategy stands for an object of some unspecified class that implements that interface. That is exactly the point of Composite: the composite does not know (or care) what kind of strategy each child is, and a child could itself be another composite.Creating the composite
Who builds this structure, and when? Larman's answer (Figs. 26.17–26.19) uses the factory from 7.1 and adds strategies at three points:
- when the sale is created, the current store discount is added;
- a new system operation,
enterCustomerForDiscount(custID), adds the customer-type discount; - when a product is entered, a product-type discount is added.
The stepper walks through the first two, which Larman draws in full.
- This is a communication diagram: objects joined by links, with numbered messages along them. At the start only the Register, the Store and the Singleton PricingStrategyFactory exist (the "1" in its corner marks a singleton).
- System operation makeNewSale() arrives at the Register (the Controller), which creates the Sale (Creator).
- The new Sale asks the Singleton factory for its pricing strategy: getSalePricingStrategy().
- The factory creates a CompositeBestForCustomerPricingStrategy. The Sale will only ever hold this one object.
- It then creates a PercentageDiscountPricingStrategy(percent) for the store's current discount and calls add(s) on the composite. That is the first of three points where strategies are added: when the sale is created.
- The second point is a new system operation, enterCustomerForDiscount(custID). The Register first turns the ID into an object: getCustomer(custID) on the Store (Expert, and "IDs to Objects").
- The Register passes the Customer to the Sale, and the Sale calls addCustomerPricingStrategy(s) on the factory, passing itself ("Pass Aggregate Object as Parameter", which supports Low Coupling and PV). Sale is used because it already knows the factory and is the Expert on its own strategy.
- The factory pulls what it needs from the Sale: the customer and the composite. It looks up getCustomerPercentage(c) itself.
- Finally it creates the customer-type strategy and adds it to the composite. (The third point, not drawn, is when a product is entered: a product-type discount is added the same way.)
Monopoly Express: three scoring rules, best one wins
Workshop 9's game has three scoring rules, and they are interchangeable strategies:
- Rule 1, applied every turn, scores complete groups.
- Rule 2, applied every 2nd turn, scores the incomplete group with the highest dice total.
- Rule 3, applied every 3rd turn, scores the sum of all placed faces.
Only the best applicable rule counts. So a factory picks the rules applicable to the turn number and puts them in a composite "highest score" strategy, which is the same design as CompositeBestForCustomer. The official partial solution to Workshop 9 (Q1.1 and Q1.2) confirms this design. In its words, the strategy pattern should be used to implement the rules, the composite pattern should be used to calculate the score when several rules apply, and the main class should use the singleton factory to determine which rules apply, based on the number of turns.
-rules 1..*. The RuleFactory Singleton returns the interface type, so MonopolyExpress never knows whether it got one rule or a composite. Static members are underlined; the provided Board and Dice packages are drawn as package tabs.Its roles in detail:
ScoreRuleis the «interface» withgetScore(board : BoardGame) : int: the Strategy interface and, at the same time, the Composite's Component.CompleteGroupRule(Rule 1),IncompleteGroupRule(Rule 2) andAllGroupRule(Rule 3) are the concrete strategies and the leaves. CompleteGroupRule also has a helper,getGroupValue(group : String) : int, which looks up a group's money points.CompositeRulerealises ScoreRule too. It holds-rules : ArrayList<ScoreRule>, drawn as an association with role-rulesand multiplicity 1..* back to the interface, and offersadd(r : ScoreRule). ItsgetScoreasks each contained rule for its score and keeps the highest.RuleFactoryis a «singleton», with a staticinstanceand a staticgetInstance(); itsgetRule(p : Player) : ScoreRulereturns the rule to use for that player's current turn.MonopolyExpress, which holds the staticmain, «use»s RuleFactory and Player, and the providedDiceandBoardpackages (and Board uses Dice).
First, the composite's link goes to the interface, not to one of the leaf rules, so a CompositeRule could in principle hold another CompositeRule. Second, getRule returns the interface type, so MonopolyExpress never knows whether it got a single rule (on turns 1, 5, 7 and so on, only Rule 1 applies) or a composite of two or three; that is exactly why Composite is combined with Strategy here. The partial solution stops at the class diagram: it does not cover the Observer-based logging engine or the Part 2 code.
Check yourselfOn turn 6 of Monopoly Express, which rules apply, and what object does RuleFactory.getRule(p) return?
Turn 6 is a multiple of 2 and of 3, so all three rules apply (Rule 1 every turn, Rule 2 every 2nd, Rule 3 every 3rd). The factory returns a CompositeRule holding CompleteGroupRule, IncompleteGroupRule and AllGroupRule, whose getScore returns the highest of the three. Its declared return type is still ScoreRule, so the caller cannot tell it apart from a single rule.
Check yourselfIn the composite pricing design, why does the strategies association go from CompositePricingStrategy to ISalePricingStrategy rather than to PercentageDiscountPricingStrategy?
Because a composite must be able to hold any strategy: percentage, absolute-threshold, or another composite. Typing the children as the interface is what makes the structure recursive and is the part markers check. Pointing at a concrete class would also break the general rule of associating to the interface, not to a concrete implementation (7.12).
- A Composite Strategy is a strategy that holds other strategies (typed to the interface) and combines their answers: min for best-for-customer, max for best-for-store or highest score.
- The context still holds one object of the interface type, and a factory builds the composite (store discount at creation, customer discount via
enterCustomerForDiscount, product discounts later). - Monopoly Express's official partial solution is the same design: ScoreRule, three leaf rules, CompositeRule, Singleton RuleFactory.
Observer / Publish–Subscribe / Delegation Event Model exam
Often one object changes and several others need to know: a window must show the new total, a log must record the change, a customer must get an email. Wiring the changing object directly to all of them couples it to every one, including classes in other layers. Observer turns this around: interested objects subscribe, and the changing object just tells "whoever is subscribed".
Intent: define a one-to-many dependency between objects, so that when one object changes state, all its dependents are notified and updated automatically.
Problem (Larman): "Different kinds of subscriber objects are interested in the state changes or events of a publisher object, and want to react in their own unique way when the publisher generates an event. Moreover, the publisher wants to maintain low coupling to the subscribers."
Solution (Larman): "Define a 'subscriber' or 'listener' interface. Subscribers implement this interface. The publisher can dynamically register subscribers who are interested in an event and notify them when an event occurs."
One pattern, many names
The pattern and its roles go by several names, and an exam may use any of them.
| Name | Means |
|---|---|
| Publisher | also called the subject or the observable: the object whose changes others care about |
| Subscriber | also called the observer or the listener: an object that wants to be told |
| "Observer" | the Smalltalk name for the pattern |
| "Delegation Event Model" | Java's name, because the publisher delegates the handling of events to listeners |
How to spot it in a scenario
The basic cue is a requirement to "inform interested parties as soon as something changes or happens", especially when there are "consumers interested only in particular sports, teams or events". In user interfaces it shows up as "the GUI must refresh when the model changes, but the model must not know the UI", or as "handle button-press events". Other cues are "log or audit every state transition", "subscribe and unsubscribe at run time", and "the number and kinds of interested objects vary".
setTotal.Participants and structure
- The Subject keeps a list of observers typed to the interface. It offers
addObserver(subscribe),removeObserver(unsubscribe) andnotifyObservers(publish). - The Observer interface declares
update(…). - The ConcreteSubject and the ConcreteObservers are the concrete classes, and each ConcreteObserver reacts in its own way.
- observers: ObserverBase list (drawn here as the association it stands for) and annotates update() as "or publish()", which tells you it is the notify operation. Right, workshop 9's version with the textbook names. In both, the subject depends only on the abstraction, and each concrete observer reacts in its own way.Larman's example: the Sale and its window
Larman makes the Sale the publisher and a GUI window, SaleFrame1, the subscriber. You saw the class diagram in the "With the pattern" tab; here is the interaction and the code.
setTotal publishes an event to whoever is registered: the last lifeline is typed by the interface, so the Sale neither knows nor cares that one of the listeners happens to be a window.public interface PropertyListener { void onPropertyEvent(Object source, String name, Object value); }
public class Sale { // Publisher
private final List<PropertyListener> propertyListeners = new ArrayList<>();
private Money total;
public void addPropertyListener(PropertyListener lis) { propertyListeners.add(lis); }
public void removePropertyListener(PropertyListener lis) { propertyListeners.remove(lis); }
public void setTotal(Money newTotal) { total = newTotal; publishPropertyEvent("sale.total", total); }
private void publishPropertyEvent(String name, Object value) {
for (PropertyListener pl : new ArrayList<>(propertyListeners)) // copy: safe if the list changes
pl.onPropertyEvent(this, name, value);
}
}
public class SaleFrame1 extends JFrame implements PropertyListener { // Subscriber
private final JTextField saleTextField = new JTextField();
public void initialize(Sale sale) { sale.addPropertyListener(this); } // subscribe
public void onPropertyEvent(Object source, String name, Object value) {
if (name.equals("sale.total")) saleTextField.setText(value.toString());
}
}
Why not let Sale tell the window directly? Because the Model–View Separation principle says domain objects must not know UI classes. Here Sale knows only PropertyListener, a UI-independent interface, and the list may even be empty. So the UI can be replaced without touching the domain, which is PV against a changing UI. Notice also that publishPropertyEvent iterates over a copy of the list; you will see why in the pitfalls below.
Observer is not only for UIs, and Larman gives other examples. GUI widget events work this way, with a JButton publishing action events. An AlarmClock publishes onAlarmEvent(source, time), and one clock can have many subscribers of different classes (AlarmWindow, Beeper, ReliabilityWatchDog), each reacting differently. In Java and .NET, the event is usually its own class (for example PropertyEvent with source, name, old value and new value), passed as the parameter.
Tony's Pizza: a complete Observer design exam-style
The week 9 case study is the course's fullest Observer design. It uses two interfaces: SubjectBase, with addObserver(o: ObserverBase) and notifyObservers(eventType: String, subject: SubjectBase), which holds 0..* observers; and ObserverBase, with update(eventType: String, subject: SubjectBase). The observers are:
- Restaurant, which observes its orders;
- Staff, who observe their restaurant (for in-app notifications) and the delivery assigned to them;
- Transaction, which places a pending charge when the order is submitted, and the real charge when delivery or pickup completes;
- Customer, who gets emails when an order is accepted, cancelled or ready for pickup;
- OrderLog, a Singleton, which timestamps every transition, including creation.
update receives both the event type and the subject: one Restaurant observes many Orders, so it must be told which one changed.Three design points are worth taking from it.
- Restaurant is both observer and subject. It is notified of an order, then notifies its own staff. The restaurant manages its staff list itself because staff change and go on and off shift.
- Observers filter. "The onus is on the observer" to check the event type and subject, and to ignore what doesn't concern it. A Customer, for example, ignores "complaint received".
- Pass the subject in
updateinstead of having the observer store one subject. One Restaurant observes many Orders, so it must be told which one changed.
- One subject (the Order) and four very different observers, plus the card-payment service. At the start, nobody has subscribed.
- Subscribe. OrderLog registers when the order is created, so even the creation is logged. The Order just appends to its observer list; it stores them as
ObserverBase, not as OrderLog. - At checkout the Customer and the Restaurant register.
- The Transaction registers only if the customer pays by card. Subscription is dynamic: the list differs from order to order.
- The event.
order.submit()changes the order's state, and the Order callsnotifyObservers("orderSubmitted", order)on itself. - Fan-out. Inside
loop [for all observers],update("orderSubmitted", order)is called on each ObserverBase. The Order does not know their classes, how many there are, or what each will do. - Each observer reacts its own way, and filters. "The onus is on the observer" to check the event type: OrderLog timestamps it; the Customer only cares about accepted, cancelled and ready-for-pickup, so it ignores this one; the Restaurant, being a subject too, notifies its staff. Inside
alt [Transaction instance], the Transaction callsCreditCardPayment.placePendingCharge(amount).
Workshop 9's Monopoly Express logging engine (writing DiceValues_{player}.txt) is the same idea, with a logger subscribing to dice-placement events.
Push versus pull
There are two ways for an observer to get the data it needs. In the push model, the notification carries the data, as in Larman's onPropertyEvent(source, name, value) or an event object. In the pull model, the notification carries the subject, and the observer asks it for what it needs, as in the course's update(subject). Push gives the observer less coupling to the subject, but can send data it doesn't need. Pull is flexible, but couples the observer to the subject's interface.
onPropertyEvent(source, name, value) pushes the value. The course's update(subject) lets the observer pull what it needs by calling the subject back (getStatus() is just an example getter).Why it works: the GRASP behind it
Observer is based on Polymorphism: each listener answers update its own way. It gives Protected Variations, because the publisher is protected from knowing the specific class and the number of objects it notifies. And its Indirection through an interface gives Low Coupling, which is what makes Model–View Separation possible.
Pitfalls exam
- Memory leaks (lapsed listeners). The publisher holds strong references, so observers that are never unsubscribed stay alive. Unsubscribe on dispose, or use weak references.
- Update order is not guaranteed, so observers must not depend on each other's order.
- Cascades and cycles. An observer that changes the subject during
updatetriggers more notifications, possibly infinitely. - Hidden cost. A trivial setter can fan out to expensive updates.
- Concurrent modification. Subscribing or unsubscribing during notification breaks the iteration, so iterate over a copy, as the Sale code above does.
- Irrelevant events. Observers receive events they don't care about and have to filter them. Separate listener interfaces per event type help.
- Implicit control flow makes debugging harder: you cannot see from the publisher's code who will run.
- Java's original
java.util.Observable/Observeris legacy. Larman notes that it was replaced by the Delegation Event Model in JDK 1.1, and it is deprecated in modern Java.
Expect it as an MCQ answer (notify many consumers as soon as something happens; interested only in some events; GUI refresh; logging every transition) and as a design in which you must show the interface, the register/subscribe operation and the notify loop. The two errors markers penalise are a publisher that depends on concrete observer classes, and no register/subscribe operation at all (7.12). Don't confuse it with Controller: system events going from the UI to the domain are Controller's job, not Observer's.
Check yourselfA sports app must alert users the moment a score changes, but each user only follows certain teams, and users can follow or unfollow at any time. Which pattern, and what are the roles?
Observer (publish–subscribe). Cues: "as soon as something changes", "interested only in particular teams", and "subscribe and unsubscribe at run time". The Match (or a score feed) is the Subject/Publisher with addObserver, removeObserver and notifyObservers; an IScoreListener interface with update(eventType, match) is the Observer; user notifiers (push notification, email, a live widget) are ConcreteObservers. Each observer filters for its own teams.
Check yourselfSpot the error: a student's Observer design has Order holding three fields, customer: Customer, log: OrderLog and restaurant: Restaurant, and Order.submit() calling a method on each. What is wrong?
The publisher depends on concrete observer classes, and there is no register/subscribe operation: both are on the markers' list. Adding a new kind of observer means editing Order. Fix: one association from Order (or SubjectBase) to the ObserverBase interface with multiplicity 0..*, an addObserver(o: ObserverBase) operation, and notifyObservers looping over the list and calling update.
Check yourselfWhy does Sale.publishPropertyEvent loop over new ArrayList<>(propertyListeners) rather than over propertyListeners itself?
A listener might subscribe or unsubscribe while it is being notified (for example, a window that closes itself on an event). Changing a list while iterating over it breaks the iteration (a concurrent-modification error). Iterating over a copy makes notification safe. This is one of the Observer pitfalls.
- Observer: the subject keeps a list of observer-interface references, offers subscribe/unsubscribe, and on each event loops calling
update; concrete observers react their own way and filter. - It keeps the publisher ignorant of the class and number of subscribers (PV, Low Coupling, Model–View Separation). Push sends the data; pull sends the subject.
- Know the pitfalls: lapsed listeners, no guaranteed order, cascades, hidden cost, modification during notification, irrelevant events, implicit control flow.
Template Method
Sometimes several classes follow the same procedure and differ only in one or two of its steps. Copying the procedure into each class duplicates code and lets the copies drift apart. Template Method writes the procedure once, in a superclass, and leaves blanks that subclasses fill in.
Intent: define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Subclasses redefine certain steps without changing the algorithm's structure.
Problem (slides): "How to build generic components that are easy to extend and use without replicating code."
Solution: implement the invariant parts of the algorithm once, in a superclass template method, and leave the varying steps to subclasses.
The slides give the Template Method solution as "Define a family of algorithms, encapsulate each one, and make them interchangeable". That sentence is GoF's intent for Strategy, not Template Method. For Template Method, use the second sentence on the slide: the invariant parts are implemented once, and the variable behaviour is left to subclasses.
The lecture example: Starbuzz coffee and tea
The coffee and tea recipes share "boil water" and "pour in cup". They differ in brewing, where coffee uses brewCoffeeGrinds and tea uses steepTeaBag, and in condiments: sugar and milk for coffee, lemon for tea.
prepareRecipe() is public and final ({leaf} in UML), so subclasses cannot change the order. The subclasses override only the protected hook methods brew() and addCondiments(). The inheritance arrow is the mechanism: no interface, no delegation.public abstract class CaffeineBeverage {
public final void prepareRecipe() { // template method: fixed skeleton, final
boilWater(); brew(); pourInCup(); addCondiments();
}
protected void boilWater() { System.out.println("Boiling water"); } // invariant steps
protected void pourInCup() { System.out.println("Pouring into cup"); }
protected abstract void brew(); // hooks / primitive operations
protected abstract void addCondiments();
}
public class Tea extends CaffeineBeverage {
protected void brew() { System.out.println("Steeping the tea"); }
protected void addCondiments() { System.out.println("Adding lemon"); }
}
Larman's vocabulary (chapter 37) names the parts. The template method is public, often final (UML {leaf}), and holds the unvarying algorithm. It calls hook methods, which are protected, either abstract or with a default implementation, and overridden in subclasses to supply the varying part. In the colour code, the abstract class is the abstraction, the template method inside it is the skeleton, and the subclasses are concrete.
- A client calls tea.prepareRecipe(). The object is a Tea, but Tea has no
prepareRecipeof its own, so the superclass's template method runs. - Step 1, boilWater(): an invariant step, implemented once in the superclass.
- Step 2, brew(): a hook. The superclass code calls it, and dynamic binding runs Tea's version. The parent is calling the child.
- Step 3, pourInCup(): invariant again, back in the superclass.
- Step 4, addCondiments(): the second hook, again supplied by Tea. That is the Hollywood Principle, "Don't call us, we'll call you": the subclass never decides when its methods run; the framework skeleton does.
This reversal of the usual direction, where the superclass calls the subclass's methods, is the Hollywood Principle: "Don't call us, we'll call you". It is the heart of (white-box) framework design: the framework owns the algorithm, and you plug your code into its hooks.
Larman's example: a persistence framework
In Larman's persistence framework, AbstractPersistenceMapper.get(oid) checks a cache; if the object isn't there, it calls the hook getObjectFromStorage(oid) and then caches the result. ProductDescriptionRDBMapper overrides the hook. The pattern is then applied again one level down: AbstractRDBMapper.getObjectFromStorage is a hook with respect to get, but a template with respect to getObjectFromRecord(oid, dbRec).
get(oid) checks a cache and calls the hook getObjectFromStorage(oid) only on a miss, then caches the result. One level down, AbstractRDBMapper.getObjectFromStorage is itself a template for the next hook. Red text marks each level's template method. This is the heart of (white-box) framework design.Two smaller examples show how common the shape is. A GUI update() { clearBackground(); repaint(); } is a template method whose hook is repaint(). And the Builder's makeJuice() from Part VI is also a template method.
GRASP and pitfalls
Template Method uses Polymorphism (through the hooks) and gives PV, because the invariant skeleton is protected and the extension points are explicit. It also supports High Cohesion, and reuse without duplicated code. Its weaknesses all come from inheritance:
- The variation is bound at compile time: there is one variation per subclass, and no swapping at run time (contrast Strategy).
- It suffers from the fragile base class problem, because changes to the skeleton ripple into every subclass.
- Make the template method
finalso that subclasses can't change the skeleton. - Too many hooks become hard to understand.
Check yourselfA report generator always does: open the data source, read the rows, format them, write the footer. Only "format the rows" differs between the CSV, HTML and PDF reports, and this is fixed when the report type is chosen. Which pattern, and which methods are final, invariant and hooks?
Template Method. An abstract ReportGenerator has a public final template method generate() that calls the steps in order. openSource(), readRows() and writeFooter() are invariant steps written once; formatRows() is a protected abstract hook overridden by CsvReport, HtmlReport and PdfReport. The cue is "same skeleton, one step varies, chosen by subclass", with no need to swap at run time; if the format had to change while running, Strategy would fit better.
Check yourselfWhat is the "Hollywood Principle", and which way does the call go in Template Method?
"Don't call us, we'll call you." In Template Method the superclass (the framework's template method) calls the subclass's hook methods, the reverse of ordinary code where a subclass calls super. The subclass only supplies the hooks; it never decides when they run.
- Template Method: a public, final template method in an abstract class fixes the algorithm's steps and calls protected hooks; subclasses override only the hooks.
- It works by inheritance (Hollywood Principle), so the variant is fixed at compile time; Strategy uses delegation and can swap at run time.
- Examples: Starbuzz
prepareRecipe, Larman'sAbstractPersistenceMapper.get, GUIupdate/repaint, Builder'smakeJuice.
Other GoF Patterns in the Textbook (Brief)
Three more patterns appear in Larman (chapters 36–37) but are not lecture topics. You won't be asked to design them, but they turn up as MCQ distractors, or as the textbook answer to a scenario, so you need to recognise each one from its problem and its shape.
Proxy (structural)
Larman (36.4) states the problem as "Direct access to a real subject object is not desired or possible." His solution reads: "Add a level of indirection with a surrogate proxy object that implements the same interface as the subject object, and is responsible for controlling or enhancing access to it." In the structure, the Client uses an «interface» ISubject, which is realised by both RealSubject and Proxy. The Proxy holds realSubject: ISubject, and its foo() does some pre-processing, calls realSubject.foo(), then does some post-processing.
There are several variants, named by what the extra step is for:
- a remote proxy is an RMI/CORBA client stub;
- a redirection proxy, also called a failover proxy, is the one drawn above:
AccountingRedirectionProxyimplementsIAccountingAdapterand holdsexternalAccounting(for example SAPAccountingAdapter) andlocalAccounting(LocalAccounting); itspostSaletries the external service and, if that fails, callslocalAccounting.postSale(sale), which stores the sale to forward later; - a virtual proxy does lazy materialisation:
ManufacturerProxyholds an OID and materialises the real Manufacturer through PersistenceFacade on first use; - a protection proxy (GoF) provides access control.
Remote-first versus local-first. Product lookups are local-first: LocalProducts implements IProductsAdapter, checks a persistent cache, and forwards misses to the remote adapter. ServicesFactory always returns this local service, so this part needs only Adapter and Factory. Accounting, by contrast, is remote-first, which is why it uses the redirection proxy. Larman also notes that one design can be several patterns at once: JavaPOS device classes are both Adapters and Proxies.
State (behavioural)
Larman (37.16) states the problem as "An object's behavior is dependent on its state, and its methods contain case logic reflecting conditional state-dependent actions. Is there an alternative to conditional logic?" The solution reads: "Create state classes for each state, implementing a common interface. Delegate state-dependent operations from the context object to its current state object. Ensure the context object always points to a state object reflecting its current state."
commit saves the object and switches it to OldClean. The state objects are stateless, so each is a Singleton (the "1" in the corner).In Larman's example, PersistentObject.commit(), rollback(), save() and delete() delegate to PObjectState, whose states are New, OldClean, OldDirty, OldDelete and Deleted. State is one way to implement a state machine in code (see Part V). It looks just like Strategy on a class diagram; 7.7 explains how to tell them apart.
Command (behavioural)
The problem (Larman 37.17) is "How to handle requests or tasks that need functions such as sorting (prioritizing), queueing, delaying, logging, or undoing?" The answer is "Make each task a class that implements a common interface" (ICommand.execute(), undo()). The examples are DBInsertCommand, DBUpdateCommand and DBDeleteCommand in a Transaction, which sorts the commands (inserts, then updates, then deletes) before executing them; GUI cut and paste with an undo history stack; and a server-side CommandProcessor. A macro command is a Composite of commands.
Check yourselfA photo-editing app must let users undo and redo every edit, and replay a saved list of edits on another photo. Which textbook pattern fits, and what is the common interface?
Command. The cues are "undo", and requests that are stored (queued, logged, replayed). Each edit becomes a class implementing ICommand with execute() and undo(); an undo history stack holds executed commands; a saved list of edits is a macro command, which is a Composite of commands. Strategy is the distractor: it swaps algorithms but gives no undo or queueing.
Check yourselfA shop's system must fetch product images from a slow remote server, but only when an image is actually displayed; until then, the rest of the system should treat the image object normally. Which pattern and variant?
Proxy, the virtual-proxy variant (lazy materialisation). An ImageProxy implements the same IImage interface as the real image, holds only an identifier, and loads the real image the first time a method such as display() is called. The client cannot tell the difference, which is the signature of Proxy: the same interface as the real subject, with controlled access.
- Proxy: a surrogate with the same interface as the real subject that controls or enhances access (remote, redirection/failover, virtual/lazy, protection).
- State: one class per state behind a common interface; the context delegates to its current state, and states switch the context to the next one. Replaces switch-on-state.
- Command: each request is an object with
execute()/undo(), so requests can be sorted, queued, logged or undone; a macro command is a Composite.
Telling Confusable Patterns Apart exam
Many patterns share the same basic trick, an interface plus polymorphism, so their class diagrams look alike. MCQ distractors are chosen precisely from these look-alikes. The way through is to ask a few sharp questions about each group: what interface does the wrapper offer, what varies and who chooses it, how many children are there. Each group below gets a side-by-side picture and the guide's comparison table.
Wrappers: Adapter vs Façade vs Decorator vs Proxy
All four of these patterns wrap other objects, which is why they get confused. The rows that separate them best are the interface the wrapper offers compared with the wrapped object's, and the typical cue.
| Adapter | Façade | Decorator | Proxy | |
|---|---|---|---|---|
| Intent | Convert an incompatible interface into the one the client expects | Simplify: one unified, high-level interface to a whole subsystem | Add responsibilities dynamically ("enhanced interface") | Control or enhance access to a real subject (remote, lazy, failover, protection) |
| Interface offered vs wrapped object | Different from the adaptee's | New, simpler; it wraps many objects | Same as the component | Same as the real subject |
| How many objects wrapped | Usually one adaptee | Many subsystem objects | Exactly one, but stackable (recursive) | One real subject; not usually stacked |
| Client awareness | Client uses the target interface | Client knows the façade | Client sees a Component, whether decorated or not | Client thinks it has the real subject |
| Typical cue | "different APIs that can't be changed" | "complex subsystem, limited use" | "optional extras in any combination" | "remote, lazy, failover or access-checked, transparently" |
The lecture summary, following GoF, puts it this way: "Adapter provides a different interface to its subject. Decorator provides an enhanced interface." GoF adds that a proxy provides the same interface as its subject. Larman adds that a resource adapter wrapping an external system is also a kind of façade; it is called an adapter when the point is adapting varying interfaces.
Algorithm variation: Strategy vs State vs Template Method
All three patterns let behaviour vary. They differ in the mechanism, in what varies, and in who chooses the variant.
| Strategy | State | Template Method | |
|---|---|---|---|
| Mechanism | Delegation (composition) to an interface | Delegation to the current state object | Inheritance: superclass skeleton plus subclass hooks |
| What varies | The whole algorithm or policy | Behaviour as a function of the object's current state | Particular steps within a fixed algorithm |
| Who picks the variant, and when | Client or Factory, at run time; usually stable for an interaction | The state objects or context switch themselves in response to events | Fixed at compile time by choosing the subclass |
| Do variants know each other? | No; they are independent | Usually yes; they set the next state | No |
| Course cue | "pricing depends on day or membership" | "behaviour depends on New/Clean/Dirty; replace switch-on-state" | "same recipe with different brew step" |
The lecture summary puts the key contrast in one line: "Template Method pattern uses Inheritance. Strategy pattern uses Delegation." The template method calls subclass methods, which is the Hollywood Principle, whereas "Calling a Strategy does not necessarily involve a structured process as in Template Method." The summary also quotes GoF: "Decorator lets you change the skin of an object. Strategy lets you change the guts."
Composite vs Decorator
Both patterns use recursive composition through a common Component type. They differ in how many children there are, in their purpose, and in what the operation does.
| Composite | Decorator | |
|---|---|---|
| Number of children | 0..* | Exactly 1 wrapped component |
| Purpose | Representation of part–whole structures, with uniform treatment of one and many | Embellishment: adding behaviour to one object |
| Operation semantics | Iterate over the children and combine the results (sum, min, draw all) | Forward to the single inner object and add something before or after |
The lecture says the two intents are "distinct but complementary" and the patterns are "often used in concert". In S2 Q7, for example, Soloist (one ISound inside, embellishing it) is shaped like a decorator, while Duet (two children) is a composite (7.10).
Creational: Factory vs Builder vs Singleton (vs Abstract Factory)
The creational patterns are easiest to tell apart by the question each one answers, and by what it hands back.
| Factory | Builder | Singleton | Abstract Factory | |
|---|---|---|---|---|
| Question it answers | Who creates an object, and which class, when creation logic is complex? | How is a complex object assembled step by step, with different representations from the same process? | How do we guarantee one instance and give global access to it? | How do we create families of related objects consistently? |
| Output | A finished object in one call, typed to an interface | One complex product, returned at the end (getResult) | Always the same instance | Several related products from one concrete factory |
| Typical partner | Singleton (the factory itself), Adapter, Strategy | Director; Template Method inside the builder | Factory, Façade, logger | Singleton plus a data-driven choice of family |
Two more pairs that show up in MCQs
Strategy versus Adapter. Both rest on polymorphism behind one interface, and both give PV. But Strategy offers interchangeable algorithms that we write ourselves, while Adapter converts existing incompatible interfaces that we can't change, so the two are not interchangeable terms. A lecture poll tested this; try it below.
Check yourselfLecture poll: which statement is false?- Strategy and Adapter can be referred to interchangeably since both are based on Polymorphism.
- Adapter is structural, Strategy is behavioural.
- Both offer PV.
- Adapter uses Indirection, Strategy doesn't.
A is false. Sharing a mechanism (Polymorphism) does not make two patterns the same: Strategy swaps algorithms we write; Adapter converts interfaces we cannot change. B and C are true. D is at best misleading, because a Strategy context also reaches its algorithm through an intermediary object, which is a form of indirection.
Strategy versus GRASP Polymorphism. Polymorphism, meaning subclasses of the domain class itself (like Payment.authorize(), "Do It Myself"), fits when the variation is fixed by the object's type. Strategy fits when there is a separate algorithm or policy object that a context delegates to and can swap at run time, such as a pricing rule or a tax calculation.
The guide used to say that "one object can play several roles" points to Strategy, and on that basis answered 2021 S2 Q1.1 (a person whose behaviour changes with the role they play) as Strategy. The official answer is Polymorphism: behaviour that varies with the kind of role is the GRASP principle in its plain form, and the stem names no algorithm being plugged into a context. So choose Strategy only when the stem describes interchangeable algorithms or policies; when it describes behaviour that varies by type or role, Polymorphism is the expected answer.
Check yourselfA text editor lets users add a spell-checker, a word counter and a grammar checker to a document view, in any combination and order, each adding something to what the view shows. Decorator or Composite?
Decorator. Each add-on wraps one view and adds to its behaviour ("optional extras in any combination"), and add-ons can wrap add-ons. Nothing here is a part–whole tree with many children whose results are combined, which is what Composite needs.
Check yourselfAn order object behaves differently when it is Draft, Submitted, Paid or Cancelled; for example cancel() refunds only when Paid, and paying moves the order from Submitted to Paid. Strategy or State?
State. The behaviour depends on the object's current state, and the variants replace each other in response to events (paying moves Submitted to Paid), so the state classes know their successors. With Strategy, the variant is chosen from outside and the strategies are independent of each other.
- Wrappers: Adapter = different interface; Façade = new simpler interface over many; Decorator = same interface, adds, stacks; Proxy = same interface, controls access.
- Variation: Strategy = delegation, whole algorithm, chosen outside; State = delegation, depends on current state, states switch; Template Method = inheritance, some steps, fixed at compile time.
- Composite vs Decorator: 0..* and combine vs exactly 1 and add. Strategy vs Polymorphism: separate swappable policy vs variation by type or role.
Pattern Recogniser exam
Q1 of the exam is ten multiple-choice questions, each a short scenario with GRASP and GoF patterns as options. You have about a minute each, so you need a fast way from the wording of a stem to a pattern. This section gives you that in three forms: a decision chart, the guide's full cue table with the distractor to watch for, and a set of practice scenarios.
Cue → pattern
Each row pairs a scenario cue with the pattern it points to. The last column names the distractor it is most easily confused with, and sometimes why that distractor is wrong.
| If the scenario says… | Pattern | Watch out for (distractor) |
|---|---|---|
| base item + choose any number of add-ons/extras/customisations; output price/ingredients/allergens/features | Decorator | Factory (only creates), Composite (no part–whole tree) |
| notify many, varied consumers when something changes; "as soon as available"; "interested only in some events"; GUI refresh; event handling; logging every transition | Observer | Controller (system events from UI → domain is Controller, not Observer) |
| several external components or drivers with different interfaces; unify them | Adapter | Façade (that is about simplifying a complex subsystem, not reconciling varying interfaces); Strategy |
| complex subsystem(s); simplified/limited use; one entry point; low impact | Façade | Controller, Adapter |
| exactly one instance; accessible throughout the system ("the Premier", simulated time-of-day, a global log, a config) | Singleton | Factory, Observer |
| varying algorithms or policies chosen by membership level, customer type, time, jurisdiction; tax, pricing, scoring, analytics variants; "experimental programs evaluated against the same tests" | Strategy | Polymorphism (GRASP; the official answer when behaviour varies by role or type, as in S2 Q1.1), Adapter |
| part–whole / tree / nested; apply to whole, some parts or one unit; "building, people and their objects"; "house, rooms, cupboard"; a service package assembled from selectable services ("hand or machine wash, vacuum, wax", the official answer to S2 Q1.9) | Composite | Decorator (adds to one object), Observer, Strategy |
| who creates these objects, and which class, when creation is complex or configurable | Factory | Creator (GRASP) when creation is simple |
| complex multi-part object built step by step; same steps, different representations; telescoping constructor | Builder | Factory |
| same algorithm skeleton, subclasses vary some steps; avoid duplicated code; framework hooks | Template Method | Strategy (swaps the whole algorithm at run time) |
| behaviour depends on current state; replace switch-on-state | State | Strategy |
| transparently fall back to a local service when the remote one fails; lazy loading; remote stub | Proxy (failover/virtual/remote) | see S1 Q1.10 below |
| requests to be queued, logged, sorted, undone | Command | Strategy |
| a family of related products per vendor or platform | Abstract Factory | Factory |
All 20 MCQs from 2021, with answer letters, reasoning and why each distractor is wrong, are worked in Part IX. That includes the ambiguous S1 Q1.10, where the remote/local database fallback is answered as Factory, following Larman ch. 36, even though the cue table above would suggest Proxy. Two of the guide's earlier answers were overturned by the official answers: S2 Q1.1 is Polymorphism and S2 Q1.9 is Composite. If two options survive, pick the more specific one that solves the whole problem (Composite over Decorator when the stem describes a tree).
Which pattern is this? Twelve practice scenarios
Cover the answer, decide in under a minute, then open it. These are new scenarios written to exercise each row of the table, not past-exam items.
Check yourselfScenario 1. A ride-sharing app computes fares differently for standard, premium and shared rides, and the company adds or changes fare rules every few months.- Polymorphism (GRASP)
- Strategy
- Adapter
- Template Method
B. Strategy. Several ways to calculate the same thing (a fare), chosen by ride type, with rules that change often: one fare-strategy class per rule behind a common interface, chosen by a factory. Adapter is wrong (nothing external with an incompatible API); Template Method varies steps of a fixed skeleton, not whole pricing policies.
Check yourselfScenario 2. A university system must read student records from three legacy databases. Each has a different API, and none can be modified.- Façade
- Adapter
- Proxy
- Strategy
B. Adapter. "Different APIs that can't be changed", to be unified behind one interface: one adapter per database, all implementing an interface we define. Façade is about simplifying one complex subsystem, not reconciling varying interfaces.
Check yourselfScenario 3. An ice-cream shop: choose a cone, then add any number of toppings from a long list. The price and the allergen list must reflect every topping chosen.- Composite
- Factory
- Decorator
- Builder
C. Decorator. Base item plus any number of add-ons, each changing the same outputs (price, allergens): each topping wraps the item and adds to it. Factory only creates; Composite needs a part–whole tree, not a base item with extras.
Check yourselfScenario 4. A smart-home hub must tell the lights, the thermostat and the phone app whenever the front door opens. New kinds of device can sign up at any time, and some only care about doors at night.- Observer
- Controller (GRASP)
- Façade
- Command
A. Observer. Notify many, varied parties when something happens, with run-time subscription and observers interested only in some events. Controller handles system events coming from the UI into the domain, which is not what is described.
Check yourselfScenario 5. A file manager must report the size of a single file, a folder, or a folder of folders, using the same call in each case.- Decorator
- Composite
- Iterator
- Strategy
B. Composite. A nested part–whole structure, an operation applied uniformly to one item or a group, and an aggregate (total size) over the parts. Decorator wraps exactly one object to add behaviour; it does not combine many children.
Check yourselfScenario 6. A new system needs only three operations from a huge, complex legacy billing subsystem, and must not be affected when the subsystem's internals change.- Adapter
- Façade
- Singleton
- Controller (GRASP)
B. Façade. "Complex subsystem, limited use, one entry point, low impact": one façade object offers the three operations and hides the rest. There is no set of varying interfaces to reconcile, so Adapter is the weaker answer.
Check yourselfScenario 7. Every component of a simulation must read the same simulated time of day, and there must never be two clocks.- Factory
- Observer
- Singleton
- Façade
C. Singleton. Exactly one instance, accessible throughout the system; the guide lists simulated time of day as a typical cue. Observer would be relevant only if components had to be told when the time changes.
Check yourselfScenario 8. A game's export classes all do the same four steps: open the file, write a header, write the rows, close the file. Only the row format differs, and it is fixed by which exporter is used.- Strategy
- Builder
- Template Method
- State
C. Template Method. Same algorithm skeleton, subclasses vary some steps, and the variant is fixed by the subclass (compile time). Strategy would swap the whole algorithm at run time.
Check yourselfScenario 9. A vending machine does different things when a coin is inserted depending on whether it is Idle, HasCoin or SoldOut, and each action can move it into another of these.- Strategy
- State
- Command
- Observer
B. State. Behaviour depends on the current state, the states replace each other in response to events, and the pattern replaces switch-on-state logic. Strategy's variants are chosen from outside and are independent.
Check yourselfScenario 10. In a sports club system, a person can be a player, a coach or a referee, and what they are allowed to do depends on which kind of role they have.- Strategy
- Polymorphism (GRASP)
- Decorator
- Composite
B. Polymorphism. Behaviour varies with the type of role, and no algorithm is being plugged into a context. This mirrors the official answer to 2021 S2 Q1.1, which chose Polymorphism over Strategy (7.7).
Check yourselfScenario 11. A travel site assembles a holiday step by step (flights, then hotel, then tours) and, from the same steps, can produce either a printable itinerary or a booking record.- Factory
- Abstract Factory
- Builder
- Composite
C. Builder. A complex multi-part object built step by step, with different representations from the same construction process; the product is returned at the end. Factory hands back a finished object in one call.
Check yourselfScenario 12. If the remote accounting service is unavailable, the POS must silently record sales locally and forward them later, without the Register knowing anything has changed.- Proxy (redirection / failover)
- Adapter
- Observer
- Command
A. Proxy, the redirection (failover) variant: Larman's AccountingRedirectionProxy (7.6). It has the same interface as the real service and decides where the call goes. Note the 2021 S1 Q1.10 stem, which asked about a remote/local database fallback, is answered as Factory following Larman ch. 36, so read the options carefully.
- First decide the family: creating, structuring/wrapping, or behaviour. Then match the strongest cue word from the table.
- Watch the GRASP look-alikes: Polymorphism (variation by type or role), Creator (simple creation), Controller (UI events into the domain).
- Know the official calibration points: S2 Q1.1 Polymorphism, S2 Q1.9 Composite, S1 Q1.10 Factory.
Past Exams: "Describe This Class" exam
One 2021 question per paper (S1 Q8 and S2 Q3, 5 marks) showed some Java and asked you to describe it. The three bullets of the question are what get marked: name the pattern and explain its problem and solution, give an application, and write usage code. Answers that only name the pattern lose most of the marks. Below are the guide's full model answers and, for each, the much shorter official answer, so you can see what the minimum is.
S1 Q8: a Singleton Counter
public class Counter {
private static final int RANGE = 1000;
private int counter = 0;
private static final Counter instance = new Counter();
private Counter() { }
public static Counter getInstance() { return instance; }
public int getNext() { return counter++; }
public void jumpToNextRange() { counter = ((counter / RANGE) + 1) * RANGE; }
}
new Counter(); (2) a private static field holding the only instance; (3) a public static getInstance(). Static members are underlined; the "1" in the corner is Larman's singleton mark. Right: what the counter does. Each block of 1000 can stand for a day, a batch or a product type.- Pattern: Singleton (GoF, creational). The problem is that exactly one instance of a class is allowed, and objects throughout the system need a global, single point of access to it. The solution has three parts: a private constructor, so no other class can call
new Counter(); a private static field that holds the only instance; and a public staticgetInstance()that returns it. - This version uses eager initialisation. The instance is created when the class is loaded (
static final … = new Counter()), which is thread-safe without synchronisation; the cost is that it is created even if it is never used. The lazy alternative checksif (instance == null)inside asynchronized getInstance(). - Behaviour.
getNext()returns the current value and then increments it, giving 0, 1, 2, … in turn.jumpToNextRange()moves the counter to the start of the next block of 1000, so a value anywhere in 0–999 jumps to 1000, and 1234 jumps to 2000. The class must be a singleton because every part of the system has to draw numbers from the same sequence; two counters would hand out duplicates. - Application. A natural use is a unique ticket, order or invoice number generator. In Tony's Pizza, for example, every terminal and every restaurant component asks the one Counter for the next order number.
jumpToNextRange()is called at the start of each day or shift, or for each new batch or branch, so that the thousands digit identifies the day or batch: day 1 uses 0–999, and day 2 uses 1000–1999. Other uses are unique IDs for log entries or database records within a process. - Usage code. Every access goes through
getInstance(), and it is always the same instance (below). - Bonus remarks (show understanding).
getNext()is not atomic (counter++), so under concurrent use it should besynchronized, or use anAtomicInteger: thread-safe creation does not make the state thread-safe. The values are also not persisted across restarts, and the global state makes unit testing harder.
Counter c = Counter.getInstance();
int a = c.getNext(); // 0
int b = Counter.getInstance().getNext(); // 1: same instance, seen from anywhere
c.jumpToNextRange(); // counter becomes 1000
int d = c.getNext(); // 1000
System.out.println(c == Counter.getInstance()); // true
// Counter x = new Counter(); // compile error: constructor is private
It agrees, and is only four sentences long, which shows how little the 5 marks need. It says the class is a singleton with eager initialisation. For the application, it suggests generating ids, or a unique suffix, for new products of different types: each product type gets its own block of numbers, which works as long as there are fewer than RANGE products per type. That is the same idea as the day-by-day order numbers, with jumpToNextRange() starting the block for a new product type. Its usage code is two lines: int id = Counter.getInstance().getNext(); and Counter.getInstance().jumpToNextRange();. The longer answer is fine if time allows, but the essentials are the pattern name with eager initialisation, one application that explains what the ranges are for, and usage that goes through getInstance().
S2 Q3: Strategy for tax
The given code is ITaxAmount.getTax(double salary) with three implementations:
PercentTaxAmounthaspercent = 0.21and returnspercent * salary;FlatTaxAmountreturns 4800 if salary > 50 000, and 0 otherwise;BracketTaxAmountapplies progressive brackets: 0 up to $20 000, then 21% of the excess over $20 000, then $12 402 + 31% of the excess over $100 000, and finally $51 667 + 45% of the excess over $180 000.
- Pattern: Strategy (GoF, behavioural). The problem is how to design for varying but related algorithms or policies (here, different ways to calculate tax on a salary), and for the ability to change them, including at run time, without
if/elseorswitchlogic in the code that needs the tax. The solution puts each algorithm in its own class behind a common interface:ITaxAmountis the Strategy interface, andPercentTaxAmount,FlatTaxAmountandBracketTaxAmountare ConcreteStrategies. - A Context holds an attribute typed to
ITaxAmountand delegatesgetTax(salary)to it. The question doesn't show one; you can supply one yourself, such asEmployeeorPayrollCalculator, though the official answer shows it is optional. The design relies on Polymorphism, gives PV (a new tax scheme is a new class, and the context doesn't change) and keeps coupling low. The strategies are typically created by a Factory, for example from configuration of jurisdiction or year. - Application. One application is a payroll system for a company employing staff under different tax regimes: different countries, employees versus contractors, or different financial years. Another is a tax-policy modelling tool that compares flat, percentage and bracket regimes on the same salaries.
- Usage code (below).
public class Employee { // Context
private final String name;
private final double salary;
private ITaxAmount taxPolicy; // typed to the Strategy interface
public Employee(String name, double salary, ITaxAmount taxPolicy) {
this.name = name; this.salary = salary; this.taxPolicy = taxPolicy;
}
public void setTaxPolicy(ITaxAmount p) { taxPolicy = p; } // swap at run time
public double getTax() { return taxPolicy.getTax(salary); }
public double getNetPay() { return salary - getTax(); }
}
ITaxAmount percent = new PercentTaxAmount();
ITaxAmount bracket = new BracketTaxAmount();
Employee e = new Employee("Ana", 120000, percent);
System.out.println(e.getTax()); // 25200.0 (0.21 * 120000)
e.setTaxPolicy(bracket);
System.out.println(e.getTax()); // 18602.0 (12402 + 0.31 * 20000)
e.setTaxPolicy(new FlatTaxAmount());
System.out.println(e.getTax()); // 4800.0
Again much shorter. It names the Strategy pattern, gives the application of working out tax on a salary depending on where the person lives, since different countries or states have different algorithms for calculating tax, and shows usage that calls the strategies directly through the interface type:
ITaxAmount tax1 = new PercentTaxAmount();
tax1.getTax(60000); // 12600.00
ITaxAmount tax2 = new FlatTaxAmount();
tax2.getTax(70000); // 4800.00
So a separate Context class is not required for the marks; the guide's earlier wording, which told you to supply one, overstated this. Adding the Employee context is still a good way to show you understand the full pattern, but the minimum is the pattern name, a jurisdiction-style application, and usage code declared with the interface type and showing concrete results.
Check yourselfUsing the S2 Q3 classes, what does new BracketTaxAmount().getTax(200000) return, and how would you write it so the variable could later hold any tax policy?
200 000 is in the top bracket: $51 667 + 45% of (200 000 − 180 000) = 51 667 + 9 000 = 60 667. Declare the variable with the interface type: ITaxAmount tax = new BracketTaxAmount(); double t = tax.getTax(200000);. Using the abstract type in usage code is one of the things markers look for.
Check yourselfWhy is the eager form of the Counter thread-safe on creation, yet the Counter still not safe to use from several threads?
The instance is created once, when the class is loaded, before any thread can call getInstance(), so two instances can never be created. But getNext() uses counter++, which is a read, an add and a write: two threads can read the same value and both return it. Thread-safe creation does not make the state thread-safe; make getNext() synchronized or use an AtomicInteger.
- "Describe this class" = pattern name with problem and solution, one application, and compiling usage code through the abstract type (or
getInstance()). - S1 Q8: Singleton with eager initialisation; ranges give per-day or per-product-type ID blocks.
- S2 Q3: Strategy; application = tax depending on jurisdiction; a Context is optional.
Past Exams: "List the GoF Patterns" (Q7.2) exam
Q7 in both 2021 papers gave about a page of Java, asked for a DCD (Q7.1) and a sequence diagram (Q7.3), and in between asked you to list the GoF patterns in the code (Q7.2, 5 marks). The DCDs and sequence diagrams are worked in Part III; here we do Q7.2. The rule is simple: name each pattern, then map every class to its role. Both papers contain the same two patterns, Composite and Strategy, and in both Strategy appears twice.
S1: the calculator
The classes are IExpr, BinaryExpr, UnaryExpr, Constant, IBinaryOp with Plus and Times, and IUnaryOp with UMinus. BinaryExpr.eval() is op.apply(left.eval(), right.eval()); UnaryExpr.eval() is op.apply(expr.eval()); Constant.eval() returns its value.
Composite. The Component is IExpr (eval()), the Leaf is Constant, and the Composites are BinaryExpr (children left, right : IExpr) and UnaryExpr (child expr : IExpr). eval() recursively evaluates the children, so an expression tree is treated the same as a single constant.
Strategy. The Strategy interfaces are IBinaryOp (apply(int,int)) and IUnaryOp (apply(int)), and the ConcreteStrategies are Plus, Times and UMinus. The Contexts are BinaryExpr (field op : IBinaryOp) and UnaryExpr (field op : IUnaryOp), which delegate the arithmetic to the operation object, so operators can be swapped or added without changing the expression classes.
As an optional extra, you can note that an expression tree with eval() is also the GoF Interpreter pattern, a specialised Composite. Composite + Strategy is the expected answer.
Same two patterns, with the marks split 2 for Composite and 3 for Strategy. For Composite it says that UnaryExpr, Constant and BinaryExpr share the interface IExpr and that Constant is the only atomic instance. For Strategy it says that IUnaryOp and IBinaryOp are the interfaces of two different instances of the pattern, so spotting both is where the third mark comes from. (The Q7.3 sequence diagram for expr.eval(), returning 9, −9, 2, −7, is in Part III.)
S2: the Musical Director's lineup
The classes are ISound, Instrument, Duet, Soloist, IDuetMusic with Jazz and Rock, and ISoloistMusic with Blues. Duet.play() returns music.playMusic(melodyInstrument, backingInstrument); Soloist.play() returns music.playMusic(instrument); Instrument.play() returns its type.
Composite. The Component is ISound (play(): String), and the Leaf is Instrument, which returns its type. The Composites are Duet, with two ISound children (melodyInstrument, backingInstrument), and Soloist, with one ISound child (instrument). Duets and soloists can nest, and any ISound is played the same way.
Strategy. The Strategy interfaces are IDuetMusic (playMusic(ISound melody, ISound backing)) and ISoloistMusic (playMusic(ISound instr)). The ConcreteStrategies are Jazz and Rock for duets, and Blues for soloists. The Contexts are Duet (field music : IDuetMusic) and Soloist (field music : ISoloistMusic), which delegate how the parts are combined to the music style.
Optionally, you can point out that Soloist wraps exactly one ISound and embellishes its output, which is the Decorator shape, but mention it only as an aside.
Example trace: duet.play() calls jazz.playMusic(guitar, saxophone). Then guitar.play() returns "Guitar" and saxophone.play() returns "Saxophone", and the result is "Jazz. Guitar melody, Saxophone backing."
It names only Composite (calling it "Composition") and Strategy. For Composite there is one mark for each class you say implements ISound, so naming Soloist, Instrument and Duet is worth three marks, and it adds that Instrument is the only atomic instance. For Strategy, ISoloistMusic and IDuetMusic are the interfaces of two different instances, and you get at most 1 mark if you identify only one. The lesson for every "list the patterns" question: name each participating class explicitly, and look for a second instance of the same pattern.
Check yourselfIn the calculator, a student writes "Composite: IExpr. Strategy: Plus, Times." How many of the 5 marks is that likely to earn, and what is missing?
Very few. It names the patterns but does not map the classes to roles. Missing for Composite: Constant is the Leaf, and BinaryExpr and UnaryExpr are Composites holding IExpr children. Missing for Strategy: the interfaces IBinaryOp and IUnaryOp, the second instance (IUnaryOp with UMinus), and the contexts BinaryExpr and UnaryExpr with their op fields. The official scheme gives the third Strategy mark for spotting both instances.
Check yourselfWhy is Soloist best described as a Composite (with a Decorator shape) rather than simply a Decorator in the official answer?
Soloist implements ISound and holds one ISound child, so it fits Composite's "composite holds Components" structure alongside Duet, and the official answer counts it as one of the three classes implementing ISound in the Composite. It does embellish a single wrapped object, which is the Decorator shape, and you may mention that as an aside; but the expected answer is Composite + Strategy.
- For Q7.2, name each pattern and map every class to a role; marks are per class.
- Both 2021 papers: Composite (IExpr/ISound component, one leaf, composites holding the component type) plus two Strategy instances (two op/music interfaces, their concrete classes, and the composites as contexts).
Past Exams: Composite Design Class Diagrams (Q9) exam
Q9 in both 2021 papers described a nested structure in words and asked for a design class diagram that answers a question about it. Both were "a clear application of the GoF Composition pattern", in the official answer's words. The work is mostly mechanical once you know the steps, and those steps are also what the markers check.
- Find the operation(s) that must work uniformly on the whole and on its parts, and put them in the Component: an abstract class or interface, with an italic or «interface» name.
- Make each atomic kind a Leaf subclass, and each grouping kind a Composite subclass.
- Draw the recursive association from the Composite back to the Component, with a diamond on the composite end, role names and multiplicities.
- Put the attributes in the right class, and put child management (
add) only in the Composite, which is the safe form. - Label the roles («Component», «Leaf», «Composite» notes) and state "Composite pattern".
- Add one-line method notes that show the recursion (for example, "sum of children's countBlack()").
S1: the quadtree image ("how many black pixels?")
Each square region is either uniformly black, white or outside the image, or it is divided into exactly four sub-quadrants: NE, SE, SW and NW. In the guide's design, Black.countBlack() returns size * size, where size is the side length in pixels, a power of two. White and Outside return 0, and Divided.countBlack() returns ne.countBlack() + se.countBlack() + sw.countBlack() + nw.countBlack().
Some design points behind it:
- The four role-named associations, each with multiplicity 1, capture "exactly four quadrants labelled NE, SE, SW, NW". The alternative is one association with multiplicity 4. On its own that loses the labels, but the official answer (below) keeps them by typing the field as a map keyed by an enumeration of the four sectors, so a single
4association is fine when paired with that map. - Composition (the filled diamond) is appropriate, because a sub-quadrant belongs to exactly one parent, although a hollow diamond, as in the generic slide, is also accepted.
- A design alternative is to pass the size as a parameter,
countBlack(side: int); the leaves then have no state and could be shared (Flyweight). QuadTreeImageis optional: it holds the root and the real image dimensions. Construction is excluded, as the question says.
colour is an enumeration, and one association with multiplicity 4, kept labelled by typing the field as a map keyed by Sector. On paper, write the braces and parentheses in the types exactly as shown.The official Component is an interface, IBWImage, with two operations, getSize() and numBlack(). There is only one leaf class, Pixel; the guide's three leaf subclasses are a defensible alternative, since the three kinds differ only in what the two operations return. Everything the checklist asks for is there: the pattern named, the recursive association typed to the Component, the exact multiplicity, and the operations explained. The official answer adds getSize(), which the question does not strictly need, so treat it as optional.
S2: a fleet of vessels
The queries are "how many sailors in the fleet?" and "make the whole fleet sail". Vessels (Ship, Yacht, Ferry) each have a number of sailors and a length, and can sail. Ships have a name, and ferries carry a number of passengers. A fleet has at least two vessels, an admiral's ship and a scouting yacht, plus any number of others.
Should Fleet be a subtype of Vessel? Composite needs Fleet to share the operations the client uses uniformly: getSailorCount() and sail(). But a fleet is not a kind of vessel. It has no single length and no crew of its own, and inheriting Vessel's attributes would break the "is-a" rule (Liskov substitution). The cleaner design puts the operations in an interface that both the abstract Vessel and Fleet realise.
length. "At least two vessels, an admiral ship and a scouting yacht" becomes two mandatory (multiplicity 1) aggregations plus 0..* others; hollow diamonds, because a vessel can exist outside a fleet. Typing others as NavalUnit allows nested fleets.Here NavalUnit is the Component, Ship, Yacht and Ferry (through the abstract Vessel) are the Leaves, and Fleet is the Composite. Fleets can be nested if others is typed as NavalUnit; type it as Vessel if nested fleets should be forbidden. Because "vessels can be added to the fleet", there is an add(), and it lives only in Fleet, which is the safe form.
0..n collection. It uses plain navigable associations, not diamonds, and repeats the overridden operations in every subclass. (It also spells the admiral field adminalVessel, a typo, corrected here.)The official answer says the question is unusual because a fleet involves at least two particular vessels, which "should be stored as separate variables in the Fleet class". Its Fleet-extends-Vessel design earns full marks, so it is the quicker choice under time pressure; if you choose it, a one-line note about the meaningless length is enough. The NavalUnit-interface version remains defensible. What both share, and what the marks rest on: the recursive link from the Composite back to the Component type, the two mandatory named vessels plus a collection, add/addVessel only in Fleet, and the pattern named on the diagram.
Check yourselfUsing the guide's fleet design, a fleet has admiral HMS A (40 sailors), scout Y (6 sailors), and others containing a ferry (12 sailors) and a sub-fleet whose admiral has 30 and scout 5. What does getSailorCount() return, and which method calls happen?
The outer fleet returns admiral + scout + sum over others: 40 + 6 + (12 + subFleet). The sub-fleet is a NavalUnit, so the same method runs on it recursively: 30 + 5 + 0 = 35. Total: 40 + 6 + 12 + 35 = 93. No code ever tests whether a unit is a vessel or a fleet; that is the point of the pattern, and it works only because others is typed as NavalUnit.
Check yourselfSpot the errors: a Q9 answer has Region (abstract) with subclasses BlackRegion and SplitRegion, an association SplitRegion → BlackRegion with no multiplicity, and addChild() declared in BlackRegion.
Three errors from the markers' list. (1) The recursive link points at a Leaf (BlackRegion) instead of the Component (Region), so a split region cannot contain another split region. (2) The multiplicity is missing; it must be exactly 4 (or four role-named links of 1). (3) Child management is placed on a leaf; it belongs only in the Composite (SplitRegion). Also check the pattern is named and the roles labelled.
- Q9 recipe: Component with the uniform operations; Leaf and Composite subclasses; recursive link Composite → Component with diamond, roles and multiplicities;
addonly in Composite; roles labelled; method notes showing the recursion. - Quadtree: exactly 4 children (four role-named links, or one link of 4 with a Sector-keyed map). Fleet: two mandatory named vessels plus a collection; Fleet-extends-Vessel earns full marks.
Common Mistakes Markers Penalise exam
Most lost marks on pattern questions come from a short list of repeated errors, some in the drawing and some in the written answer. Read the list once now, and again the day before the exam; the pictures show the three most common diagram errors next to their fixes.
The full list
- Wrong arrowheads. Realisation (a dashed line with a hollow triangle), generalisation (a solid line with a hollow triangle) and association (an open arrow) must be kept distinct. Workshop 11 warns that markers "will not be guessing" what an arrow means.
- Associations to concrete classes when the context should hold an interface. Sale should point to ISalePricingStrategy, not to PercentDiscount….
- Composite errors: a missing recursive link, a link pointing at Leaf, a missing multiplicity (0..*, or exactly 4 for the quadtree), the Composite not realising or extending the Component, and child operations placed on the leaves.
- Decorator errors: a decorator that doesn't implement the component interface or doesn't hold the wrapped component (multiplicity 1), and describing plain subclassing as "Decorator".
- Singleton errors: a public constructor, a non-static instance or
getInstance, or a missing private constructor, and forgetting thread safety when asked about lazy initialisation. - Observer errors: a publisher that depends on concrete observer classes, or the lack of any register/subscribe operation.
- Strategy errors: having no context object, or choosing the strategy with a
switchinside the context, which defeats the purpose. - "Describe this class" answers that only name the pattern. You must cover problem and solution, an application and usage code, which are the three bullets in the question. The code should compile and use the abstract type.
- "List the patterns" answers that don't map classes to roles. Each pattern use needs its elements named.
- Pattern-itis. Larman ch. 36 and the workshops warn that patterns force-fitted to trivial problems over-complicate the design, so justify every pattern by the variation or problem it addresses.
Check yourselfA student's Observer answer for a news app has NewsAgency with fields emailSender: EmailSender and smsSender: SmsSender, and a publish() that calls both. List every error a marker would flag and the fix.
(1) The publisher depends on concrete observer classes. (2) There is no register/subscribe operation, so the set of subscribers is fixed at compile time. Fix: an interface INewsListener with update(…), realised by EmailSender and SmsSender (dashed line, hollow triangle); one association NewsAgency → INewsListener with multiplicity *; operations subscribe, unsubscribe and a publish() that loops over the list.
Check yourselfWhich arrow would you draw from CompositeRule to ScoreRule in the Monopoly Express design, and which one for the rules list? (There are two.)
CompositeRule implements the ScoreRule interface: a dashed line with a hollow triangle (realisation). It also holds a list of ScoreRules: a solid line with an open arrowhead (navigable association) with role -rules and multiplicity 1..*. Drawing only one of the two, or swapping their styles, loses the Composite marks.
- Draw the right arrow for the right relationship, and point associations at interfaces.
- Composite: recursive link to the Component with a multiplicity; Composite is a Component; child operations only in Composite. Singleton: private constructor, static instance and getInstance. Observer: interface plus subscribe. Strategy: a context with an interface-typed field, no switch.
- Written answers: problem, solution, application and compiling usage code; map every class to a role; justify every pattern.