GRASP: Assigning Responsibilities
Once you know what the system must do, you still have to decide which software object does what. GRASP gives you nine named principles for making that decision methodically, and in this subject every design answer is expected to name them.
- You willtell doing from knowing responsibilities, and see why drawing a message is assigning one.
- You willknow the problem, solution, benefits and contraindications of all nine GRASP principles.
- You willbuild a design sequence diagram for a system operation and justify every message by a principle.
- You willpick the right principle in a multiple-choice question and write justifications that earn marks.
Object Design, Responsibilities and Patterns
Analysis (Parts I and II) told you what the system must do. Object design answers the next question: which software object does what? This section sets the scene: what object design starts from, what a "responsibility" is, and why naming design ideas as patterns helps.
Question 1 of the exam has multiple-choice items of the form "pick the pattern that best fits this design problem". GRASP names appear both as the right answers and as distractors (Controller, Information Expert, Low Cohesion, Polymorphism, Indirection, …).
Beyond that, every design question, whether it asks for a design sequence diagram, a design class diagram or "describe this class", expects you to justify who does what by naming the principle.
Where object design starts
Object design does not start from a blank page. Each analysis artefact you met earlier hands the designer something specific, and the output for one scenario is called a use case realisation: the UP term for how one use case scenario is carried out in the Design Model by collaborating objects.
makeNewSale, enterItem, …) becomes the first message into the domain layer, and some object must receive it. That object is the controller (4.5). You draw one interaction diagram per system operation.Name and structure the software classes after the domain concepts (Board, Square, not AB324), so the code mirrors how people think about the domain. LRG is why the domain model is the first place you look for candidate classes.
Even so, a design is not a one-to-one simulation of the real world. You may add classes that are not in the domain model, drop domain classes, and use different multiplicities. The Monopoly design below does all three kinds of thing; NextGen POS does the third, because the software Store creates just one Register.
Piece because Player can hold its own location, and turn associations into navigable arrows. Why each change was made is explained in 4.7 and 4.6.The design also follows Model-View Separation: domain objects do not do output, and UI objects contain no application logic. So when the use case says "System displays total", the design only has to make sure that some object knows the total. Showing it is the UI's job.
Larman's underlying point is that UML is only notation. The real design tool is a mind trained in design principles, and GRASP gives those principles names.
Responsibilities: doing and knowing exam
The UML defines a responsibility as "a contract or obligation of a classifier". Informally, it is what an object is obliged to do or to know because of its role. That gives two kinds.
Sale has a time, so the software Sale knows its time (that is LRG at work).A responsibility is not a method. It is an abstraction, and methods fulfil it. Its size varies a lot: "create a Sale" is one method, while "provide access to relational databases" might take a subsystem of hundreds of classes.
A method can fulfil a responsibility alone or by sending messages to other objects, which is called collaboration. For example, Sale.getTotal fulfils "know the sale total" by asking each SalesLineItem for its subtotal.
Responsibility-Driven Design (RDD)
RDD is a metaphor: treat software objects like people who have responsibilities and collaborate with others to get work done. An OO design is then a community of collaborating, responsible objects.
In practice you decide responsibilities while drawing interaction diagrams (and while coding). There is no separate "assign responsibilities" document: every arrow you draw is a decision about who does what, and the design class diagram then records those decisions in its method compartments.
makePayment arriving at :Sale, and :Sale sending create to :Payment, is the decision "Sale is responsible for creating Payments". The DCD (right) records it as a method of Sale and a navigable association to Payment. Violet labels like by Creator name the GRASP principle behind a message; this page uses that colour for them throughout.What a pattern is
Experienced designers keep reusing the same handful of good ideas. Writing those ideas down in a standard form, with a name, lets beginners use them too.
A pattern is a named, well-known problem/solution pair that can be applied in new contexts, ideally with advice on how to apply it and a discussion of its trade-offs (Larman ch. 17). The lecture's own definition: "a recurring successful application of expertise in a particular domain".
The name matters. It helps you hold the idea in memory, and it lets you describe a complex design in one sentence, such as "put a Facade over the persistence subsystem and use an Abstract Factory for the mappers".
The lecture lists five advantages of patterns:
- they capture expertise in a standard form that non-experts can use;
- that expertise is easy to re-use;
- they improve understandability;
- they give practitioners a common vocabulary;
- you can build new solutions by modifying existing patterns.
Because patterns codify old, proven knowledge, "new pattern" is an oxymoron. GRASP likewise introduces no new ideas: it names principles experts already use.
Larman's pattern format describes every pattern under the same headings. For each GRASP principle, the three you must be able to state are the Problem, the Solution and the Contraindications.
Problem
The design question the pattern answers, e.g. "Who should create an A?"Solution
The advice: what to do, e.g. "B creates A if B contains A".Contraindications
When not to use it, because it would make things worse.Name · Example · Discussion · Benefits · Related
A handle, a worked case, subtleties, what you gain, and neighbouring patterns.GoF refers to the 23 patterns of Design Patterns by Gamma, Helm, Johnson and Vlissides (the "Gang of Four", 1994), such as Adapter and Strategy (Part VI). The pattern idea itself came from Christopher Alexander's building architecture and was brought to software by Kent Beck and Ward Cunningham.
GRASP: the nine at a glance exam
General Responsibility Assignment Software Patterns: nine patterns, or principles, for assigning responsibilities to objects so that design reasoning is methodical, rational and explainable. Whether you call each one a pattern or a principle does not matter ("one person's pattern is another person's primitive building block").
Larman calls GRASP a learning aid, but in this subject you are marked on using the names correctly.
The nine do not all do the same job. One is the root goal, two are the yardsticks you measure designs with, three tell you who gets a particular kind of responsibility, and three are mechanisms for reaching the goals.
| Principle | Problem it answers | Solution (in brief) | Role |
|---|---|---|---|
| Information Expert | What is a general principle for assigning a responsibility? | Give it to the class that has the information needed to fulfil it | Default assignment rule |
| Creator | Who creates an instance of A? | Class B, if B contains/compositely aggregates, records, closely uses, or has the initialising data for A | Assigning creation |
| Controller | What is the first object beyond the UI layer that receives and coordinates a system operation? | An object representing the whole system, a root object, a device or a subsystem (façade controller), or the use case scenario (use-case/session controller) | Assigning system events |
| Low Coupling | How to support low dependency, low change impact and more reuse? | Assign so that (unnecessary) coupling stays low. Use it to evaluate alternatives | Evaluative goal |
| High Cohesion | How to keep objects focused, understandable and manageable (and, as a side effect, support Low Coupling)? | Assign so that cohesion stays high. Use it to evaluate alternatives | Evaluative goal |
| Polymorphism | How to handle alternatives based on type? How to make pluggable components? | Give the varying behaviour, as polymorphic operations, to the types that vary. Don't test for type | Mechanism |
| Pure Fabrication | Who gets a responsibility when Expert (etc.) would hurt cohesion or coupling? | Invent an artificial, non-domain class with a highly cohesive set of responsibilities | Mechanism |
| Indirection | Where to put a responsibility so that two things are not directly coupled? | Give it to an intermediate object that mediates between them | Mechanism |
| Protected Variations | How to design elements so that variation or instability in them does not harm other elements? | Identify points of predicted variation or instability; wrap them in a stable interface | Root goal |
Check yourselfClassify each as a doing or a knowing responsibility: (a) Sale knows its time; (b) Sale creates SalesLineItems; (c) SalesLineItem knows its ProductDescription; (d) Register coordinates the enterItem system operation; (e) Register asks Sale to make a line item.
(a) knowing: its own encapsulated data. (b) doing: doing something itself (creating an object). (c) knowing: knowing about a related object. (d) doing: controlling and coordinating activities in other objects. (e) doing: initiating action in another object.
Check yourselfA classmate says "a responsibility is just a method". What is wrong with that, and when do responsibilities actually get decided?
A responsibility is an abstraction; methods fulfil it, alone or by collaborating with other objects. Its granularity varies: "create a Sale" may be one method, "provide access to relational databases" may be a whole subsystem. Responsibilities are decided while drawing interaction diagrams (and while coding): drawing a message to an object assigns that responsibility, and the DCD records it.
Check yourselfWhich two GRASP principles are evaluative, and which is the root goal? Why might an MCQ list "Low Cohesion" as an option?
Low Coupling and High Cohesion are evaluative: you use them to compare alternative designs. Protected Variations is the root goal. "Low Cohesion" names the problem rather than the principle; when a question describes a class that does too many unrelated things and asks what is wrong, "Low Cohesion" is the right label for the problem.
- Object design turns analysis artefacts into a use case realisation: one interaction diagram per system operation, plus a DCD.
- Software classes take domain names (LRG), but the design may add, drop and reshape classes.
- Responsibilities are doing or knowing; drawing a message assigns one.
- GRASP names nine principles: one root goal (PV), two evaluative goals (LC, HC), three assignment rules (Expert, Creator, Controller) and three mechanisms (Polymorphism, Pure Fabrication, Indirection).
Creator exam
Creating objects is one of the most common things an OO program does. If every class could create anything, you would get coupling everywhere. Creator gives a consistent rule for who creates, and the rule is chosen so that creating adds no new coupling.
Who should be responsible for creating a new instance of some class A?
Give class B the job of creating A if any of these hold (the more, the better):
- B contains or compositely aggregates A;
- B records A;
- B closely uses A;
- B has the initialising data for A (then B is the Expert for creating A).
If several classes qualify, prefer the one that contains or aggregates A.
How to apply it
B and A are software classes. Look first for existing design classes that fit the role of B. If there are none yet, borrow from the domain model (LRG) and mirror its containment in the design.
The reason it works is simple: the chosen creator is normally connected to the new object anyway, because it contains, records or uses it. Making it the creator therefore adds no new coupling.
The lecture works through the checklist for Monopoly's Square, which makes a good template for exam answers.
In the diagrams, Creator shows up twice: as a create message in the sequence diagram (the dynamic view) and, often, as composite aggregation in the DCD (the static view).
create message points at the new object's box, drawn where it is created.The standard examples
Each standard example is justified by one of the four criteria. In an exam answer, always name the criterion, not just the principle.
| Object created | Creator | Criterion |
|---|---|---|
Monopoly Square | Board | Board compositely aggregates Squares |
POS SalesLineItem | Sale (via makeLineItem(desc, qty)) | Sale contains (aggregates) line items |
POS Sale | Register | Register records Sales (a business register has always recorded transactions) |
Monopoly Player | MonopolyGame | Has the initialising data (player names from user input) |
Payment initialised with the sale total | Sale | Has the initialising data (it knows the total) |
Library BorrowedItem | BorrowingRecord | Contains/compositely aggregates BorrowedItems |
Tony's Pizza Pizza, OrderLineItem | Order | The order ultimately contains them |
In Tony's Pizza the Order itself had no good domain creator (making the Customer create it felt wrong), so the controller's startOrder() creates it and records it as currentOrder.
Contraindications and benefits
If creation is complex, hand it to a helper Concrete Factory or Abstract Factory (Part VI) instead of the class Creator suggests. "Complex" means things like recycling instances for performance, or conditionally creating one of a family of similar classes based on some external property or context.
Benefits: Creator supports Low Coupling, which means fewer maintenance dependencies and more reuse. Related: Low Coupling, Factory, Whole-Part.
Check yourselfA Payment must be initialised with the sale total. Which class should create it, and by which Creator criterion?
Sale, because it has the initialising data: it knows its total. (Sale also closely uses its Payment, and choosing Sale keeps coupling low, as 4.4 shows.)
Check yourselfIn the library system, a BorrowingRecord contains one or more BorrowedItems, and the BorrowingKiosk records BorrowingRecords. Who creates a BorrowedItem?
BorrowingRecord, by Creator, because it contains (compositely aggregates) BorrowedItems. The kiosk records records, not the items inside them, so it is not the natural creator of BorrowedItems.
Check yourselfA system must create one of several kinds of tax-calculator adapter depending on a setting in a configuration file. Is Creator the right answer for "who creates the adapter"?
No. This is conditionally creating one of a family of similar classes based on an external property, which is Creator's contraindication. Use a Factory (a Pure Fabrication) instead.
B creates A if B contains, records, closely uses, or has the initialising data for A; prefer containment. It adds no new coupling, because B is already connected to A. For complex or conditional creation, use a Factory instead.
Information Expert exam
A real design has hundreds of classes and thousands of responsibilities. You need a default rule for placing them that tends to produce designs that are easy to understand, maintain, extend and reuse. Information Expert ("Expert") is that rule.
What is a general principle for assigning responsibilities to objects?
Give the responsibility to the information expert: the class that has the information needed to fulfil it. This works for both doing and knowing responsibilities.
Where to look: if suitable classes already exist in the Design Model, look there first. Otherwise look in the Domain Model and create a matching design class (LRG).
The procedure exam
Expert is the principle to consider first, unless the problem is a creation problem (use Creator) or a system-event problem (use Controller). Applying it takes four steps. Step through them for Larman's classic question, "who knows the sale total?"
- Step 1: state the responsibility clearly. "Who should be responsible for knowing the sale total?" The candidate software classes come from the domain model:
Sale,SalesLineItem,ProductDescription. None has any methods yet. - Step 2: summarise the information needed. The total is the sum of the line-item subtotals, and each subtotal is quantity × price.
- Step 3: tabulate who has each piece.
Saleknows all its line items (through thelineItemsassociation);SalesLineItemhas thequantity;ProductDescriptionhas theprice. The information is spread over three classes, so there will be three partial experts. - Step 4a: Sale knows the total. Sale is the expert on the whole (it has all the line items), so
getTotal()goes toSale, by Expert. The DCD gains+getTotal(): Money. - Step 4b: each line item knows its subtotal. Sale cannot compute a subtotal itself: the quantity lives in
SalesLineItem. So Sale loops over its line items and asks each one,getSubtotal(), by Expert. - Step 4c: the description knows the price. The line item has the quantity but not the price, so it asks its
ProductDescription,getPrice(), by Expert. - The partial experts collaborate. Each class does the part it has the data for, and the answers flow back: price → subtotal (quantity × price) → total (sum of subtotals). No class had to reach into another's data.
The code follows the diagram line for line: each method is on the class that owns the data it uses.
public class Sale {
private List<SalesLineItem> lineItems = new ArrayList<>();
public Money getTotal() { // Sale knows all its line items
Money total = new Money(0);
for (SalesLineItem sli : lineItems) total = total.add(sli.getSubtotal());
return total;
}
}
public class SalesLineItem {
private int quantity;
private ProductDescription description; // association to its description
public Money getSubtotal() { return description.getPrice().times(quantity); }
}
Key ideas
Partial experts collaborate. When the information is spread across several objects, each does its part and they pass messages to share the work. The sale total needed three.
Expert embodies Coad's "Do It Myself" or animation principle: a software object does the things that would normally be done to the inanimate real-world thing it represents. A paper receipt doesn't add itself up, but a software Sale does. In a company, the analogy is giving a task to the person who has the information, as when the CFO prepares the profit-and-loss statement.
Expert supports Low Coupling. Put the responsibility anywhere other than with the information, and the information has to be shipped away from its home, which adds dependencies. The pair below makes that visible.
Sale, which it knows anyway (it is the current sale).More course examples
Every Expert answer has the same shape: "X does it, because X has Y". The table collects the ones from the course.
| System | Responsibility | Expert | Because it has… |
|---|---|---|---|
| Monopoly | getSquare(name) | Board | all the Squares (it aggregates them) |
| POS | getProductDescription(itemID) | ProductCatalog | all the descriptions (it contains them) |
| POS | becomeComplete() | Sale | the isComplete attribute |
| Library | hasReachedLimit() | Member | borrowLimit, and its association to its BorrowingRecords |
| Tony's Pizza | addTopping, removeTopping, editSize | Pizza | its own toppings and size |
When there are several partial experts, place the responsibility with the dominant expert, the one with most of the information. If no expert dominates, compare coupling and cohesion. If that is still a tie, consider likely future evolution (Monopoly's takeTurn, 4.10, is the worked case).
Contraindications exam
Expert says Sale, since it has the data. But that design has four problems:
Salenow contains SQL/JDBC code that has nothing to do with "being a sale": it is incohesive;- it is coupled to a technical service (JDBC) rather than to other domain objects: it is highly coupled;
- every persistent class would repeat similar code: duplication and poor reuse;
- domain logic and database logic are mixed, which violates separation of concerns and widens the representational gap.
The fix is a separate, invented class, PersistentStorage: a Pure Fabrication, drawn in 4.7.
Benefits: Expert preserves encapsulation, because objects use their own information, and that usually gives Low Coupling. It also spreads behaviour over lightweight classes, which usually gives High Cohesion. Also known as: "Place responsibilities with data", "That which knows, does", "Do It Myself", "Put services with the attributes they work on".
Check yourselfThe balance of a sale is the payment amount minus the total. Sale and Payment both hold part of that information. Who should compute it, and why?
Sale.getBalance(). Both are partial experts, so compare coupling: Sale already has visibility to its Payment (it created it), whereas putting the method in Payment would give Payment new visibility to Sale. "By Expert and Low Coupling."
Check yourselfYou must design "who checks whether a library member has reached their borrowing limit?". Apply the Expert procedure.
(1) State it: who knows whether a member has reached the limit? (2) Information needed: the member's borrowLimit and how many items they currently have borrowed. (3) Who has it: Member has borrowLimit and is associated with its BorrowingRecords. (4) Assign hasReachedLimit() to Member, by Expert. 4.11 shows why the alternatives (kiosk, record) cost more coupling.
Check yourselfSpot the flaw: "By Information Expert, Sale should contain the JDBC code that saves it, because it has all the data."
This is Expert's textbook contraindication. Saving makes Sale incohesive (database work is not sale-ness), couples it to a technical service (JDBC), duplicates the same code in every persistent class, and mixes domain and DB concerns. Give the job to a Pure Fabrication such as PersistentStorage.
Give a responsibility to the class that has the information. State the responsibility, list the information, tabulate who has it, assign each part to its expert; partial experts collaborate. Expert is the default rule (try it first unless it is a creation or system-event problem), but it backfires for technical services like persistence.
Low Coupling and High Cohesion exam
Expert, Creator and Controller tell you where to try putting a responsibility. Low Coupling and High Cohesion are how you judge the result, and how you choose when two answers seem equally valid. They are a pair: Larman calls them "yin and yang", because a design that is bad on one is usually bad on the other.
Coupling
Coupling is how strongly one element is connected to, has knowledge of, or relies on other elements. If A depends on B, a change to B may force a change to A.
A class with high coupling suffers in three ways: it is forced to change when the classes it depends on change; it is hard to understand in isolation; and it is hard to reuse, because everything it depends on has to come along with it.
How to support low dependency, low change impact and increased reuse? (Short form in ch. 17: "How to reduce the impact of change?")
Assign responsibilities so that (unnecessary) coupling stays low. Use this principle to evaluate alternatives.
Five common forms of coupling from TypeX to TypeY exam
"Coupling" sounds vague until you can point to it in a diagram or in code. These are the five forms to recognise, each with a course example.
Worked comparisons
Low Coupling is used to choose between designs. Three comparisons from the course show how. First, Board versus an arbitrary Dog for getSquare in Monopoly.
Second, who computes a line's subtotal? ProductDescription knows the price, so Expert alone might pick it. But then Sale, when summing, would have to talk to ProductDescription as well: unnecessary coupling. Low Coupling therefore prefers SalesLineItem, which knows its quantity and its description.
Third, the comparison most often examined: who creates the Payment? Step between the two tabs and count the arrows in the dependency graphs.
Payment (a new, red dependency), and Sale still has to know Payment to store it. Register also takes on one more piece of work.makePayment; Sale creates the Payment. Nothing new is coupled, because Sale must know Payment anyway. So Low Coupling and Creator suggest different answers, and Low Coupling wins. High Cohesion prefers this design too, because Register does less.The slides say "By Expert, Register should be responsible for creating Payment". The textbook attributes the Register suggestion to Creator (Register records Payments). Use Creator in your answers; the final answer, Sale, is the same either way.
Discussion and contraindications
Low Coupling is an evaluative principle that you apply to every design decision, and it cannot be considered in isolation from Expert and High Cohesion. Subclassing is strong coupling, so choose it carefully: a PersistentObject superclass that every domain class extends gives them persistence for free, but couples domain objects to a technical service and mixes architectural concerns.
There is no absolute measure of when coupling is "too high"; instead, ask whether increasing it will cause problems. Generic classes that are likely to be reused should have especially low coupling. And less is not always better:
High coupling to stable, pervasive elements is rarely a problem; coupling to java.util, for instance, is fine. The problem is coupling to elements that are unstable in their interface, their implementation or their mere presence. Effort spent lowering coupling with no realistic motivation is wasted, whereas NextGen's tax-calculator interfaces really do vary, so lowering coupling there pays off.
Benefits: elements are unaffected by changes in other components, simple to understand in isolation, and convenient to reuse. Related: Protected Variations. Coupling and cohesion as design principles were identified by Larry Constantine.
Cohesion
Cohesion (strictly, functional cohesion) is how strongly related and focused the responsibilities of an element are. An element with highly related responsibilities that does not do a huge amount of work has high cohesion, so both the amount of code and how related it is matter. Booch: elements that "all work together to provide some well-bounded behavior".
A low-cohesion class does many unrelated things, or simply too much. It is hard to understand, reuse and maintain, and it is delicate: constantly affected by change. Such classes are often very coarse-grained, or have taken on work they should have delegated.
How to keep objects focused, understandable and manageable, and as a side effect support Low Coupling?
Assign responsibilities so that cohesion stays high. Use it to evaluate alternatives.
Levels of cohesion exam
Larman describes four levels, from very low to high. Notice that the remedy at the two lowest levels is to split the class.
Examples
Monopoly's playGame shows the difference between doing the work and delegating it.
playRound is a small, single-purpose method named in domain vocabulary.In POS, having Register create Payment is harmless on its own. The point is the trend: if Register does part of the work for each of the ~50 system operations it receives, it becomes a bloated, incohesive object. Delegating Payment creation to Sale gives higher cohesion and lower coupling. (The library version of this argument, hasReachedLimit in the kiosk, is in 4.11.)
Bad cohesion usually brings bad coupling, and vice versa. A GUI widget that paints itself, saves to a database and calls remote services is incohesive and coupled to many unrelated elements. Modular design means decomposing a system into cohesive, loosely coupled modules (Booch), and you get there with single-purpose methods and classes that group related concerns.
- Grouping for a specialist. Put all the embedded SQL in one class,
RDBOperations, so the one SQL expert can maintain it in one place. - Distributed or remote server objects. Remote calls are expensive, so use fewer, larger, less cohesive server objects. This is the Coarse-Grained Remote Interface pattern: one
setData(...)call instead ofsetName,setSalaryandsetHireDate.
The slides generalise this: low cohesion may be needed to meet strict non-functional requirements such as performance.
Benefits: clearer, easier-to-understand design; simpler maintenance and enhancement; Low Coupling is often supported; fine-grained, focused classes are easier to reuse.
Check yourselfName the form of coupling in each: (a) class CashPayment extends Payment; (b) Money getTotal() in Register, which returns currentSale.getTotal(); (c) private ProductCatalog catalog; in Register.
(a) subclass, the strongest form: CashPayment is coupled to Payment. (b) two forms at once: Register calls a service of Sale, and uses Money as a return type. (c) an attribute referring to a ProductCatalog.
Check yourselfTrue or false: "The ideal design has no coupling at all, so every class can be reused on its own."
False. Coupling taken to the extreme goes against the OO metaphor of collaborating objects and produces a few bloated, incohesive active objects plus passive data holders. Moderate coupling is normal and necessary. Coupling to stable elements (like java.util) is rarely a problem; worry about coupling to unstable ones.
Check yourselfA ReportManager class formats reports, sends emails, validates user passwords and caches database rows. Which cohesion level is this, what is the remedy, and which MCQ label fits?
Very low cohesion: one class is solely responsible for many things in very different functional areas. Split it into separate families of classes. If a question asks what is wrong with it, the label is Low Cohesion (the problem); the principle that fixes it is High Cohesion.
Check yourselfA team merges setName, setSalary and setHireDate on a remote employee server into one setData(...). Has it broken High Cohesion, and is that acceptable?
It has lowered cohesion slightly, deliberately. It is a recognised contraindication: remote calls are expensive, so a Coarse-Grained Remote Interface with fewer, larger operations is justified by a performance requirement.
- Coupling = dependence on other elements; five forms (attribute, call, type use, subclass, implements). Keep unnecessary coupling low, especially to unstable things.
- Cohesion = how focused an element's responsibilities are; split classes that cover unrelated areas or do too much.
- Both are evaluative: use them to choose between alternatives (Payment is created by Sale, not Register). Both have contraindications (stable libraries; performance).
Controller exam
When a cashier presses "End Sale", a window receives the click. Something in the domain must then actually end the sale. If the window did that work itself, the business logic would be stuck inside one UI technology. Controller decides which domain-side object receives each request from the UI.
A system event (or system operation) is a major input event to the system, typically from a GUI operated by an actor (a cashier pressing "End Sale"); external messages and sensor signals also count. These are the operations on the SSD. A controller is the first object beyond the UI layer that receives and handles a system operation message.
What first object beyond the UI layer receives and coordinates ("controls") a system operation?
Give the responsibility to a class representing one of:
- the overall "system", a "root object", a device the software runs in, or a major subsystem: all variants of a façade controller;
- a use case scenario in which the system event occurs: a use case (or session) controller.
Façade controller examples: Register (the POS terminal device), POSSystem, Store (root), MonopolyGame (root), Phone, BankCashMachine, ChessGame. Use case controllers are conventionally named <UseCaseName>Handler, …Coordinator or …Session: ProcessSaleHandler, PlayMonopolyGameHandler, OrderHandler. Use the same controller class for all system events in the same use case scenario, so it can keep that use case's state. (A session is one conversation with an actor, often organised as a use case session.)
Corollary: "window", "view" and "document" classes are not on the list. They receive UI events and delegate to a controller; they must not fulfil system operations themselves. Compare the two designs for enterItem.
enterItem, received by the controller, which hands the real work to Sale.Sale directly, so application logic lives in the presentation layer. Swap Swing for a web page, or run the system in batch mode, and this logic has to be rewritten.In code, the window converts input and delegates; the controller coordinates without doing the work itself.
// UI layer: the window only converts input and delegates
public class ProcessSaleJFrame extends JFrame {
private Register register; // reference to the domain-layer controller
public ProcessSaleJFrame(Register register) { this.register = register; }
private void onEnterItemClicked() { // button click handler
ItemID id = Transformer.toItemID(txtId.getText());
int qty = Transformer.toInt(txtQty.getText());
register.enterItem(id, qty); // crosses from UI layer to domain layer
}
}
// Domain layer: the controller coordinates, it does not do the work
public class Register {
private ProductCatalog catalog;
private Sale currentSale;
public void makeNewSale() { currentSale = new Sale(); } // Creator
public void enterItem(ItemID id, int qty) { currentSale.makeLineItem(catalog.getProductDescription(id), qty); }
public void endSale() { currentSale.becomeComplete(); }
public void makePayment(Money cash) { currentSale.makePayment(cash); }
}
Controller is a delegation pattern: it acts as a kind of façade onto the domain layer from the UI layer, and it should coordinate and do little itself. Tony's Pizza calls it a "postbox": its methods correspond to system operations and just call the right domain object.
Façade controller versus use-case controller exam
| Façade controller | Use-case (session) controller | |
|---|---|---|
| Represents | Whole system, root object, device or subsystem | One use case scenario |
| Names | Register, POSSystem, Store, MonopolyGame | ProcessSaleHandler, HandleReturnsHandler, OrderHandler |
| Choose when | There are not too many system events, or the UI cannot route messages to different controllers (e.g. a message-processing system) | The façade would become bloated (low cohesion, high coupling); many system events across different processes |
| Extra | Often a domain concept (LRG) | Not a domain object: a Pure Fabrication; a natural place to track use-case state |
Larman chose Register for NextGen and MonopolyGame for Monopoly because each has only a few system operations (Monopoly has just two, initialize and playGame). In Tony's Pizza the presenter chose a use-case controller, OrderHandler, because one controller for the whole system (ordering, deliveries, ingredients) would get bloated.
OrderHandler keeps the state of the ordering use case (the order in progress, 0..1) and offers one method per system operation. It reduces the coupling from the GUI to the domain to a single point.Benefits, bloat and the cure
Benefits. (1) Reuse and pluggable interfaces: application logic is not bound to a window, so it can be reused, the UI can be replaced, or the system can run in batch mode. (2) You can reason about the state of the use case: for example, enforcing that makePayment cannot happen before endSale is easiest when one controller handles the whole use case.
Three signs that a controller has become bloated:
- a single controller receives all system events, and there are many of them (common with façade controllers);
- the controller performs the work itself instead of delegating, which usually violates Expert and High Cohesion;
- it has many attributes and keeps significant system or domain information that belongs to other objects, or duplicates information found elsewhere.
Two cures: (1) add more controllers, using use-case controllers instead of one façade; (2) design the controller so it mainly delegates.
MakeReservationHandler, ManageSchedulesHandler, ManageFaresHandler, each passing the work on (green arrows) to the objects that own the data.Things not to confuse
- UI layer does not handle system events exam. That is why a JFrame, a web page or a
mainmethod must forward the event to a domain controller. - GRASP controller ≠ Web-MVC controller. The MVC controller (for example a Struts Action) belongs to the UI layer and controls page flow. The GRASP controller belongs to the domain/application layer and does not know which UI technology is in use.
- UP terms: boundary objects are interface abstractions, entity objects are application-independent (often persistent) domain objects, and control objects are use-case handlers as in this pattern.
- In message-handling systems such as a telecom switch, each message is typically represented and handled by a Command object (GoF).
Related: Command; Façade (a façade controller is a kind of Façade); Layers; Pure Fabrication (a use-case controller is one). The lectures covered Controller only in workshop supplementary material (Workshop 4, Exercise 4; Tony's Pizza week 4), so the textbook (ch. 17.13) is the authority.
Check yourselfA student's design has CheckoutPage (a web page) call cart.calculateDiscount() and order.create() directly. Which principle is broken and how do you fix it?
Controller (and its corollary): a UI object is fulfilling a system operation. Add a domain-layer controller (for example a CheckoutHandler use-case controller, or a façade such as Store) that receives the system operation, say checkout(...), from the page and delegates to the domain objects. The page only converts input and forwards it.
Check yourselfA vending-machine program has three system operations. Another system, a hospital, has over sixty, across admissions, billing and pharmacy. Which kind of controller suits each?
Vending machine: a façade controller representing the device (like Register), because there are few system events. Hospital: use-case controllers (e.g. AdmitPatientHandler, BillingHandler), because one façade would become bloated, with low cohesion and high coupling.
Check yourselfAn MCQ describes "one handler class that receives every request and does most of the processing itself". Which GRASP answers fit?
It describes a bloated controller, so Controller is the principle at stake (cure: use-case controllers plus delegation). If the options phrase it as the problem, the answer is Low Cohesion.
Check yourselfIs a Struts Action (a Web-MVC controller) a GRASP controller?
No. It belongs to the UI layer and controls page flow. A GRASP controller sits in the domain/application layer and is independent of the UI technology; the Struts Action would forward the system operation to it.
The first object beyond the UI that receives a system operation is a controller: a façade (system, root, device, subsystem) when there are few events, a use-case controller (a Pure Fabrication) when a façade would bloat. UI objects never handle system events, and controllers coordinate and delegate rather than do the work.
Polymorphism exam
Whenever behaviour depends on what kind of thing you have, the tempting design is an if or switch on its type. That works until a new kind arrives, and then every such conditional, often scattered across many classes, has to be found and edited. Polymorphism removes the conditionals.
(1) How to handle alternatives based on type? (2) How to create pluggable software components, so that one server component can replace another without affecting the client?
When related alternatives or behaviours vary by type (class), give the responsibility for the behaviour to the types for which it varies, using polymorphic operations: operations with the same interface (name and signature) in different classes.
Corollary: do not test for the type of an object and use conditional logic to choose behaviour. Also known as "Choosing Message" and "Don't Ask 'What Kind?'".
Monopoly squares
Landing on Go gives you $200, Income Tax charges you tax, a Regular square does nothing, and Go To Jail moves you to jail. The types that vary are the kinds of square, and the varying operation is "what happens when a player lands", so it is named landedOn. Square types are domain concepts too, so the domain model gets a generalisation hierarchy as well.
// bad design (the corollary forbids this) SWITCH ON square.type CASE GoSquare: player receives $200 CASE IncomeTaxSquare: player pays tax ...
Every new kind of square means editing this switch, and any other place that switches on square type.
Square and its landedOn are written in italics because they are abstract (you may write {abstract} instead). Each subclass overrides landedOn, so the caller just sends the message and the right version runs.public abstract class Square {
public abstract void landedOn(Player p); // {abstract}: no sensible default
}
public class GoSquare extends Square {
public void landedOn(Player p) { p.addCash(200); } // Player is Expert on its cash
}
public class RegularSquare extends Square {
public void landedOn(Player p) { } // NO-OP avoids special-case logic
}
public class IncomeTaxSquare extends Square {
public void landedOn(Player p) { p.reduceCash(Math.min(200, p.getNetWorth() / 10)); }
}
public class GoToJailSquare extends Square {
private Square jail; // set at initialisation
public void landedOn(Player p) { p.setLocation(jail); }
}
Design decisions in the dynamic model
Who sends landedOn? The Player, at the end of takeTurn: by Expert and Low Coupling, it already knows its location square. The player passes itself as a parameter, landedOn(p), which gives the square parameter visibility to the player, so it can send addCash, getNetWorth, reduceCash or setLocation, each assigned to Player by Expert.
Drawing polymorphic cases. You can draw a separate sequence diagram per subclass, or use sd/ref frames. The lecture and Tony's Pizza use an alt frame guarded by the runtime type, as below. That guard only documents which implementation runs; the code contains no type test.
landedOn(p); four implementations. The square calls back to the player it was given as a parameter. Note the RegularSquare branch: an empty method is still a real method.RegularSquare.landedOn is an empty (NO-OP) method. It exists so that the client never has to special-case anything. The general guideline: declare the polymorphic operation {abstract} in the superclass unless there is a sensible default behaviour.
Refactoring for coupling. In iteration 1 the Piece knew the location, and Player kept asking it. When object A keeps needing B's data, either A should hold that data or B should have the responsibility (Expert). So in iteration 2 Player holds location directly, and Piece disappears from the design.
Other course examples
NextGen tax calculators. The third-party calculators (Tax-Master, Good-As-Gold TaxPro, …) have different interfaces: raw TCP socket, SOAP, Java RMI. Adapting differs by calculator type, so each adapter implements a polymorphic getTaxes(Sale): List<TaxLineItem> from the interface ITaxCalculatorAdapter. Each adapter is a local software object that calls its external calculator through that calculator's native API. (The full design is stepped through in 4.8.)
Tony's Pizza. Pizza and Drink both have getPrice(): double but compute it differently.
Item with abstract getPrice(); OrderLineItem holds exactly one Item. New item types can be added without changing OrderLineItem or Order.Library. When videos as well as books can be borrowed, an Item interface (getTitle, getDetail, getQuantity) is implemented by Book and Video, and getDetail is polymorphic.
When to design with interfaces
Use an interface when you want polymorphism without committing to a class hierarchy. With only an abstract superclass, every new implementation must subclass it, which is limiting in single-inheritance Java. A rule of thumb: for an abstract superclass C1, consider an interface I1 holding C1's public signatures and have C1 implement I1. That leaves an evolution point for future cases.
Avoid speculative "future-proofing" with interfaces and polymorphism at points where variation is improbable. Be realistic about how likely the variation is before paying for flexibility.
Benefits: new variations are easy to add, and new implementations can be introduced without affecting clients. Related: Protected Variations; many GoF patterns rely on polymorphism, including Adapter, Command, Composite, Proxy, State and Strategy (and Decorator).
Check yourselfA Shape class has draw() containing if (type == CIRCLE) … else if (type == SQUARE) …. What does Polymorphism say, and what changes when a triangle is added?
Don't test for type: make draw() a polymorphic (abstract) operation of Shape and give each subclass (Circle, Square) its own version. Adding a triangle is then one new subclass, Triangle, with no edits to existing conditionals or to the code that calls draw().
Check yourselfAn MCQ describes a person whose behaviour changes depending on the role they are playing (for example student or tutor). Which GRASP answer was given as official?
Polymorphism: this is the official answer to 2021 Semester 2 Q1.1. Behaviour that varies by the role (type) an object plays goes into polymorphic operations. Prefer the GoF answers only when the stem is more specific: Strategy for interchangeable algorithms for one context object, State when behaviour changes with the object's state.
Check yourselfWhy does RegularSquare need an empty landedOn method at all? Couldn't the player just skip calling it?
Skipping the call would mean the player tests whether the square is a regular one: exactly the type test the corollary forbids. The NO-OP method lets every square be treated the same way, so the client never special-cases anything.
When behaviour varies by type, give each type its own version of one polymorphic operation and never switch on type. An alt frame may document the cases; the code doesn't test. Use interfaces for pluggability, but only where variation is realistic.
Pure Fabrication and Indirection
Expert works when the information and the behaviour naturally belong together. Sometimes they don't: saving to a database, rolling dice for any game, or talking to an outside system. Forcing those jobs onto domain classes ruins their cohesion and coupling. These two principles give such jobs a home.
Pure Fabrication exam
Which object should have the responsibility when you don't want to violate High Cohesion and Low Coupling, but the solutions offered by Expert (and other patterns) are not appropriate?
Give a highly cohesive set of responsibilities to an artificial or convenience class that does not represent a problem-domain concept, made up purely to support high cohesion, low coupling and reuse.
Ideally the fabrication's design is very clean, or "pure"; the name also plays on the English idiom "pure fabrication", meaning something made up. On this page, invented classes are shaded green.
Example 1: saving a Sale exam
A real persistence framework uses many classes behind such a front-end façade (Part VI).
Example 2: rolling the dice (Cup)
In iteration 1, Player rolled and summed the dice. That had two problems: dice are general objects useful in many games, but the summing service was tied to Monopoly's Player; and you could not ask for the current total without rolling again. Every other domain-inspired class has the same problems (the slides note that by the domain model and Expert, MonopolyGame would be the candidate, which is acceptable but gives low cohesion). So the design fabricates a Cup that holds the dice, rolls them and knows their total. Larman still picks domain-flavoured vocabulary, since real games use dice cups.
Board finds the new square because it knows all the squares (Expert); the square reacts by its own type (Polymorphism). Larman labels key messages with their principle, as here.public class Cup { // not in the Monopoly domain model
private List<Die> dice = new ArrayList<>();
private int total;
public void roll() { // command: changes state, returns nothing
total = 0;
for (Die d : dice) { d.roll(); total += d.getFaceValue(); }
}
public int getTotal() { return total; } // query: can be asked without re-rolling
}
Example 3: the library's BorrowingHandler
In the library system, BorrowingKiosk is the controller that receives UI input. That leaves the question of who runs the application logic (newBorrowing, returnItem, getItemAvailability). With High Cohesion and Low Coupling in mind, none of the existing domain classes fits, so the design fabricates one.
You have already met other Pure Fabrications: use-case controllers, Factories (Part VI) and Adapters such as TaxMasterAdapter. In fact Larman says all GoF patterns are Pure Fabrications.
Representational versus behavioural decomposition
Pure Fabrication rests on a distinction between two ways of carving up a design.
A class represents a domain thing: Sale, TableOfContents. This supports LRG.
A class groups related behaviour or an algorithm: TableOfContentsGenerator, PersistentStorage, Cup. Pure Fabrications are function-centric, behavioural objects.
Labelling a class "Pure Fabrication" is not important in itself. The point is that some classes come from the domain and some are invented for the designer's convenience.
Novices who think in functions tend to overuse Pure Fabrication, so that "functions just become objects". The slides warn that it should not be an excuse to add new objects, and that overuse leads to one class per responsibility.
Overuse also hurts coupling: too many behaviour objects whose responsibilities are separated from the information they need increases coupling. The symptom to watch for: most of an object's data is passed to other objects to reason with. Balance Pure Fabrication with Expert, so representational classes like Sale keep their responsibilities.
Benefits: High Cohesion, because responsibilities sit in a fine-grained, focused class, and more potential for reuse. Related: Low Coupling, High Cohesion. A Pure Fabrication usually takes over a responsibility that Expert would have given to a domain class.
Indirection
Indirection is about decoupling: rather than letting two things depend on each other directly, put something in between them.
Where to assign a responsibility so that two (or more) things are not directly coupled? How to decouple objects so that low coupling is supported and reuse potential stays high?
Give the responsibility to an intermediate object that mediates between the other components or services, so they are not directly coupled. The intermediary creates an indirection between them.
The standard examples
TaxMasterAdapter exam. The external Tax-Master system is reached over TCP sockets. If Sale talked to it directly, Sale would contain all the TCP code: it would depend on one specific protocol and system (high coupling) and mix sale and communication responsibilities (low cohesion). Instead, Sale asks an adapter.
«actor» :TaxMasterSystem.PersistentStoragemediates betweenSaleand the database, so it is both a Pure Fabrication and an Indirection.- Library.
Membersits betweenBorrowingKioskandBorrowingRecord: the kiosk asks the member (hasReachedLimit,newBorrowing) rather than reaching the records itself. This intermediary is a domain class, not a fabrication. - Controller. A controller is an indirection between the UI layer and the domain layer. A client-side controller also lets services be provided either locally or remotely.
Indirection versus Pure Fabrication
The lecture distinguishes them like this: Indirection means any class acting as an intermediary, whereas Pure Fabrication means an artificial, non-domain class. They overlap a great deal.
Discussion. The motivation for Indirection is usually Low Coupling. An old adage says most problems in computer science can be solved by adding another level of indirection; a counter-adage says most performance problems are solved by removing one. Many GoF patterns are specialisations of Indirection: Adapter, Bridge, Façade, Observer and Mediator.
Benefits: lower coupling between components. Related: Protected Variations, Low Coupling, Pure Fabrication.
Check yourselfWhy is Cup a Pure Fabrication even though dice cups exist in real games?
It is not a concept in the Monopoly domain model; it was invented to fix design problems: dice summing tied to Player (poor reuse) and no way to read the total without re-rolling. Larman deliberately gave it a domain-flavoured name, but it exists for cohesion and reuse, which is what makes it a fabrication.
Check yourselfA design has Sale holding only data, plus SaleTotaller, SaleCompleter and SalePaymentMaker, each of which reads Sale's fields to do its job. What is wrong?
Overuse of Pure Fabrication: "functions just become objects", one class per responsibility. The symptom is exactly the warning sign: most of Sale's data is passed to other objects to reason with, which increases coupling and leaves an anaemic Sale. By Expert, those responsibilities belong in Sale itself.
Check yourselfIs every Indirection a Pure Fabrication? Give an example either way.
No. In the library, Member is an intermediary between BorrowingKiosk and BorrowingRecord but is a genuine domain class. TaxMasterAdapter and PersistentStorage, on the other hand, are both: intermediaries and invented classes.
- Pure Fabrication: when Expert would wreck cohesion or coupling, invent a cohesive non-domain class (
PersistentStorage,Cup,BorrowingHandler). Don't overuse it. - Indirection: put an intermediary between two things so they aren't directly coupled (
TaxMasterAdapter,Member, a controller). - They overlap heavily; Indirection is about position, Pure Fabrication about origin.
Protected Variations exam
Some parts of a system are bound to change: an external API, a business rule, a hardware device. If the rest of the system depends on those parts directly, every change ripples outwards. Protected Variations (PV) is the root principle of GRASP: find the unstable spots and wall them off.
How to design objects, subsystems and systems so that variations or instability in these elements do not have an undesirable impact on other elements?
Identify points of predicted variation or instability; assign responsibilities to create a stable "interface" around them. "Interface" is meant broadly, as a means of access, not only a Java interface.
Points of change. A variation point is a variation in the existing system or current requirements, such as the several tax-calculator interfaces that must be supported now. An evolution point is a speculative variation that may arise in future but is not in the current requirements; in the UP these can be documented as Change Cases. PV applies to both.
Example: NextGen tax calculators exam
Applying PV takes two steps: find the instability, then build the stable interface. Here two mechanisms build it, Indirection and Polymorphism.
- The problem. NextGen must use several third-party tax calculators, each with its own interface (raw TCP socket, SOAP, Java RMI, …), and more may come. If
Saletalked to them directly, it would be stuffed with protocol code (low cohesion) and depend on every external API (high coupling). - PV step 1: identify the point of instability. It is the different interfaces, or APIs, of the external tax calculators, including future ones that don't exist yet. This is a variation point: the current requirements already demand several.
- PV step 2a: Indirection. Put an adapter object in front of each calculator. Each adapter is a local software object that calls its external system through its native API, so Sale is shielded from the protocols. The adapters are also Pure Fabrications.
- PV step 2b: Polymorphism.
getTaxesbehaves differently for each calculator, so it becomes a polymorphic operation of the interfaceITaxCalculatorAdapter, which every adapter implements.Salenow depends only on this stable interface (the dashed «use» arrow). - The payoff. A new calculator means one new adapter class implementing the interface, with no change to
Sale. In the lecture's words: indirection protects against variation of interfaces; polymorphism protects against variation of behaviours.
Library example. The requirement "a member may borrow videos as well as books" makes Book a known point of variation. PV puts a stable Item interface around it, and Polymorphism handles the type-specific behaviour (getDetail).
PV is the root principle
Data encapsulation, interfaces, polymorphism, indirection, standards, virtual machines, operating systems, configuration files: all are mechanisms for PV. The lecture summary: PV, Low Coupling and High Cohesion are the fundamental goals; the other six GRASP patterns are ways to achieve them. The mechanisms motivated by PV are these.
Core mechanisms
Data encapsulation, interfaces, polymorphism, indirection, standards. Virtual machines and operating systems are complex indirections.Data-driven designs
Read codes, values, class names, style sheets, property files or O-R mapping metadata from outside, so behaviour is "parameterised" at runtime.Service lookup
Naming or lookup services (JNDI, UDDI) protect clients from where services are located. A special case of data-driven design.Interpreter-driven designs
Rule interpreters, script engines and VMs externalise the logic, which is read in and interpreted.Reflective designs
Introspection, e.g. the Java beansIntrospector or Method.invoke.Uniform access
Same syntax for field access and method call (C#, Eiffel, Ada), so fields can become methods without changing clients.Standard languages
SQL protects against many varying query languages.Liskov Substitution Principle
Below.Structure-hiding designs
In particular the Law of Demeter, below.Liskov Substitution Principle (LSP) exam
Code written against a type T (an interface or abstract superclass) must keep working as expected when given any subtype or implementation S of T. Formally (Liskov 1988), S is a subtype of T if substituting an S object for a T object leaves the behaviour of every program written in terms of T unchanged. In the tax example, addTaxes must work whichever adapter it is passed:
public void addTaxes(ITaxCalculatorAdapter calculator, Sale sale) {
List<TaxLineItem> taxes = calculator.getTaxes(sale); // must work for EVERY implementation
}
Law of Demeter: "Don't Talk to Strangers" exam
This was one of the nine GRASP patterns in Larman's first edition. It was replaced by the more general PV because it is a special case of it: protection against changes in object structure. Within a method, send messages only to:
this(self);- a parameter of the method;
- an attribute of
this; - an element of a collection that is an attribute of
this; - an object created within the method.
Those objects are the familiars; objects reached indirectly are strangers. Traversing long paths couples the client to how objects are connected, and the further the path, the more fragile the code.
sale) to get the information and hide how it did so.class Register {
private Sale sale;
void slightlyFragile() {
Money amt = sale.getPayment().getTenderedAmount(); // talks to a stranger (Payment)
}
void moreFragile() {
AccountHolder h = sale.getPayment().getAccount().getAccountHolder(); // even further
}
void conforming() {
Money amt = sale.getTenderedAmountOfPayment(); // familiar hides the structure
}
}
To obey the law strictly, you add operations to the familiars that return the desired information and hide how they got it. That isn't always worth it: structure in stable libraries (Java's) or in mature systems seldom changes. It matters most in young systems, where the structure is still changing.
PV, information hiding and the Open-Closed Principle
These are essentially the same idea under three names.
- Information hiding (Parnas 1972) means hiding design decisions that are difficult or likely to change from other modules. It is not a synonym for data encapsulation, which is only one technique for it.
- The Open-Closed Principle (Meyer): modules should be open for extension (adaptable) and closed to modification that affects clients. "Closed with respect to X" means clients are unaffected if X changes. A class with private fields and public accessors is closed with respect to its field definitions but open to changing them; the tax adapters are closed with respect to their public interface (
ITaxCalculatorAdapter) but open to private changes as external APIs evolve. "Module" means any software element: method, class, subsystem or application.
PV itself was named as a pattern by Cockburn (1996), but the idea is decades older.
Contraindications: speculative PV exam
Protecting an evolution point can cost more than simply reworking a simple, "brittle" design when the change actually comes. Larman's examples are an unneeded scripting interpreter in a pager system, and his own early "generalize-itis" of writing superclasses for future cases that never arrived.
Benefits: new variations are easy to add; new implementations don't affect clients; coupling is lowered, and so are the impact and cost of change. PV is related to almost every principle and pattern (polymorphism, interfaces, indirection, encapsulation, most GoF patterns). Pree calls variation and evolution points "hot spots".
Check yourselfInside Register.endSale(), which of these break the Law of Demeter? (a) sale.becomeComplete(); (b) sale.getPayment().getAmount(); (c) Payment p = new Payment(cash); p.getAmount(); (d) catalog.getProductDescription(id).getPrice().
(a) is fine: sale is an attribute. (b) breaks it: Payment is reached through Sale, a stranger. (c) is fine: an object created within the method. (d) breaks it: the ProductDescription returned by the catalog is a stranger. (Whether to fix (d) depends on how stable that structure is.)
Check yourselfA team adds a plug-in scripting engine to a simple pager system "in case the rules change one day". Which principle is being over-applied, and what is the counter-argument?
Protected Variations, applied speculatively to an evolution point (Larman's own example). Protecting it may cost more than reworking a simple design if the change ever comes. Pick your battles: balance the cost of change against its likelihood.
Check yourself"Information hiding just means making fields private." True or false?
False. Parnas meant hiding design decisions that are difficult or likely to change, which is PV. Data encapsulation (private fields) is only one technique for it.
Check yourselfIn the tax design, which mechanism protects Sale from the different APIs, and which from the different behaviour of each calculator?
Indirection (the adapters) protects against variation of interfaces; Polymorphism (getTaxes on ITaxCalculatorAdapter) protects against variation of behaviours. Together they give the stable interface that PV asks for.
Find points of predicted variation (current variation points or speculative evolution points) and put a stable interface around them. PV is the root goal; encapsulation, interfaces, polymorphism, indirection, LSP and the Law of Demeter are its mechanisms, and it is the same idea as information hiding and the Open-Closed Principle. Don't protect against changes that won't realistically come.
How the Principles Fit Together
You have now met all nine. They are not independent rules: some are means to others, and some regularly disagree. This section maps how they relate, collects the standard conflicts, and links GRASP to the GoF patterns of Part VI.
The relationship map
The lecture shows the relationships as a diagram in which each arrow means "is a way to achieve".
| Principle | Is a way to achieve | Why |
|---|---|---|
| Low Coupling | Protected Variations | Fewer dependencies mean variation spreads less |
| Polymorphism | Low Coupling (and PV directly) | Clients depend only on the common interface |
| Indirection | Low Coupling | An intermediary removes the direct dependency |
| Pure Fabrication | Low Coupling and High Cohesion | Moves alien responsibilities out of domain classes |
| Information Expert | Low Coupling and High Cohesion | Behaviour stays with its data |
| Controller | Indirection | Sits between the UI layer and the domain layer |
| Creator | Information Expert | Creation goes to whoever has or holds the data |
Conflicts and trade-offs exam
Principles often disagree, and the exam rewards naming the conflict and saying how it was resolved. Read each row from the two competing principles across to the resolution, and notice how often the deciding factor is cohesion, coupling or stability.
| Situation | One principle says | Another says | Resolution |
|---|---|---|---|
Save a Sale to the DB | Expert: Sale has the data | HC/LC: SQL makes Sale incohesive and coupled to JDBC; duplicated in every class; mixes concerns | Pure Fabrication PersistentStorage (also an Indirection) |
Create Payment | Creator: Register records Payments and has the amount tendered | LC: adds Register→Payment coupling. HC: Register drifts towards bloat | Sale creates it (it closely uses Payment). "By Creator and Low Coupling" |
| Compute line subtotal | Expert: ProductDescription has the price | LC: then Sale must also know ProductDescription | SalesLineItem (knows quantity and its description) |
| Compute balance | Sale and Payment are both partial experts | LC: Payment would need new visibility to Sale | Sale.getBalance(), because Sale already sees its Payment |
| Monopoly "take a turn" | Real world: a human takes a turn | Expert: Player, MonopolyGame and Board each hold about a third of the info | Player, justified by HC (MonopolyGame already busy) and future evolution (cash, buying strategy), not by imitating humans |
| One controller for everything | Controller: a root/system object is valid | HC: many system events → bloated | Use-case controllers (OrderHandler) |
| Log completed sales | LRG/Expert: Store knows its sales | HC: Store grows incohesive | Store early on; SalesLedger (a real domain concept) when Store grows |
| UI shows running total | LC: UI talks only to Register, which forwards getTotal | HC: Register's interface keeps growing | UI may couple directly to Sale, because coupling to a stable class is fine |
| No coupling at all | LC taken to the extreme | OO metaphor / HC | Moderate coupling is normal; couple to stable things |
| Interfaces "just in case" | PV / Polymorphism | Cost and complexity | Only at realistic variation points |
| Remote/performance-critical objects | HC: fine-grained classes | Performance NFR | Accept coarser, less cohesive objects |
| Many helper classes | Pure Fabrication: cohesive helpers | Expert/LC: data separated from behaviour | Keep responsibilities in representational classes where Expert works |
When there are alternatives, look closely at the cohesion and coupling of each and at likely future evolution, and choose the one with good cohesion, coupling and stability under likely change.
GRASP and the GoF patterns exam
The GRASP principles generalise the GoF patterns, so each GoF pattern can be described in GRASP terms. This is also how mixed GRASP/GoF multiple-choice options are built.
Check yourselfCreator says Register should create Payment; the final design has Sale create it. Write the one-sentence justification an examiner wants.
"Sale creates Payment by Creator (Sale closely uses Payment and has the initialising data) and Low Coupling: Register then never needs to know Payment, whereas Register-creates-Payment would add a Register→Payment dependency and push Register towards bloat (High Cohesion)."
Check yourselfDescribe the Observer pattern in GRASP terms.
It is an Indirection giving Low Coupling between publisher and subscribers, and it uses Polymorphism through a common listener interface. (Like every GoF pattern it is also a Pure Fabrication.)
Check yourselfWhy is Controller drawn as a way to achieve Indirection on the map?
Because a controller sits between the UI layer and the domain layer: the UI depends on the controller instead of directly on many domain objects, which is exactly what an intermediary does.
PV is the root goal; Low Coupling and High Cohesion are the evaluative goals; the other six are ways to achieve them. When principles conflict, compare cohesion, coupling and stability under likely change, and say which principle won and why. Every GoF pattern can be described in GRASP words.
Worked Use-Case Realisations (Larman ch. 18) exam
Larman's chapter 18 puts all the principles to work on complete use cases. This is exactly what an exam design question asks you to do: take a system operation and draw the design sequence diagram, justifying each message. Here is the general procedure, then NextGen POS and Monopoly.
The procedure for designing a system operation
- Take the system operation from the SSD. By Controller, choose the domain object that receives it, and draw the found message into that object.
- Work through the operation contract postconditions (Part II). An instance creation calls for Creator. An association formed is usually done by the creator, or by whoever has visibility. An attribute modification calls for Expert: the object that owns the attribute.
- For every other responsibility, state it as a question ("Who knows X?") and apply Expert. Tabulate the information needed against the classes that have it.
- Evaluate the alternatives with Low Coupling and High Cohesion, and then with future evolution (PV).
- If Expert's choice hurts cohesion or coupling (technical services, generic behaviour), use Pure Fabrication or Indirection. If behaviour varies by type, use Polymorphism.
- Check visibility for every message: attribute, parameter, local or global (Part III).
- Ignore display and output (Model-View Separation); just make sure the information is known.
- Design Start Up last. Once you know what must exist, pick an initial domain object at or near the root of the containment hierarchy (
Store,MonopolyGame). It creates its children by Creator. - Annotate messages with the principle used ("by Creator", "by Expert", …) and update the DCD.
NextGen POS: Process Sale
The controller is Register, a façade device controller. That is reasonable because there are only four system operations: makeNewSale, enterItem, endSale and makePayment. (In UML the system operation is a found message arriving from outside the diagram, drawn from a filled dot.)
makeNewSale: the Register records Sales, so it creates them (Creator). endSale: setting isComplete is an attribute modification, so it goes to its owner (Expert). getBalance is not a system operation, but any message can start its own interaction diagram; getTotal is designed as in 4.3.- Controller. The system operation
enterItem(id, qty)from the SSD arrives as a found message at :Register, the façade controller. Register will coordinate, not do the work. - Find the description, by Expert. Register asks
:ProductCatalogfordesc = getProductDescription(id): the catalog contains all the descriptions. Register can send this message because it has permanent attribute visibility to the catalog, set up at Start Up. - Delegate to the Sale. Register sends
makeLineItem(desc, qty)to the current:Sale, which it knows through itscurrentSaleattribute. The contract's postconditions (a line item created and associated with the sale) are about the sale's contents, so the sale should handle them. - Create the line item, by Creator. Sale contains (aggregates) its line items, so Sale creates
sl : SalesLineItem, passing the description and quantity as initialising data. Thecreatearrow points at the new object's box. - Form the association. Sale adds
slto itslineItemscollection, completing the "association formed" postcondition. Done by the creator, which already has visibility to the new object.
- Controller.
makePayment(cashTendered)arrives at :Register, the same controller as for every other Process Sale operation, so it can enforce the order (no payment beforeendSale). - Delegate. Register forwards
makePayment(cashTendered)to the current sales. It does not create the payment itself. - Create the Payment, by Creator and Low Coupling. Sale creates
:Payment, passing the cash tendered. Sale closely uses its Payment, and choosing Sale rather than Register adds no new coupling (4.4). - Log the sale, by Expert (and LRG). Register sends
addSale(s)to:Store, because the store knows its completed sales. (If Store grew incohesive, aSalesLedger, a real domain concept, could take over.) Finally, the balance is computed bySale.getBalance()as shown above.
Putting the four operations together gives this design class diagram. Every method in it was put there by one of the decisions above.
Register's four are the system operations (Controller); makeLineItem and makePayment on Sale are Creator; getTotal, getSubtotal, getPrice, becomeComplete, getBalance and addSale are Expert. Each navigable arrow exists because some message needed that visibility.Connecting the UI to the domain
In main, you create the initial domain object Store, get its Register, and pass that to the window: new ProcessSaleJFrame(store.getRegister()). Alternatively the UI gets the domain object from a factory. To show the running total, the UI has two options. It can ask the Register, which keeps UI coupling lower but makes Register bigger and less cohesive. Or it can get the Sale and ask it directly: more coupling, but acceptable because Sale is stable. Larman takes the second option.
- Pick the initial domain object. Start Up is designed last, once you know what must exist.
Storesits at the root of the containment hierarchy, so it is created first. - Store creates the catalog, by Creator. The store contains the product catalog.
- The catalog loads its descriptions, by Creator. For each product, the catalog creates a
ProductDescriptionand puts it in itsdescriptionsmap. It contains them, so it creates them. - Store creates the Register, passing the catalog.
create(pc)gives the Register permanent attribute visibility to the catalog, whichenterItemneeded. Note that multiplicities in the design may differ from the domain: the software Store creates one Register. - Hook up the UI.
mainpasses the store's Register to the window, so every UI event goes to the controller.
Monopoly: Play Monopoly Game
Here the controller is MonopolyGame, a root-object façade, which works because there are only two system operations. The design proceeds in three decisions.
The game loop. The loop is "for N rounds: for each player p: p takes a turn". By Expert, MonopolyGame runs it, because it knows the round count (LRG) and all the players. A private helper playRound() keeps the methods cohesive and uses domain vocabulary.
Who takes a turn? This is the course's main example of partial experts with none dominant.
Inside takeTurn. Die rolls itself, by Expert, since it changes its own faceValue. Board.getSquare(oldLoc, fvTot) finds the new square, by Expert, since Board knows all the Squares. The Piece (or Player) updates the location. Player coordinates all of this, so it needs permanent visibility to the dice, Board and Piece, set up at Start Up.
- Controller.
playGame()arrives at:MonopolyGame, a root-object façade controller: fine, because Monopoly has only two system operations. - The game loop, by Expert. The game knows the number of rounds and all the players, so it runs
loop [rnd < N], calling its private helperplayRound()(method-level cohesion, domain vocabulary). - Each player takes a turn. Inside
playRound, the game sendstakeTurn()to each player. Player was chosen from three partial experts using High Cohesion and future evolution, as in the figure above. - Roll the dice, by Expert. Each
Diechanges its ownfaceValue, so it rolls itself; the player then asks for the face value and sumsfvTot. (Iteration 2 replaces this loop with theCup, 4.7.) - Where am I? In iteration 1 the
:Pieceknows the location, so the player asks it:oldLoc = getLocation(). - Find the new square, by Expert.
:Boardknows all the squares, sonewLoc = getSquare(oldLoc, fvTot)goes to the board. - Move. The player tells the piece
setLocation(newLoc). Because Player keeps asking Piece for its data, iteration 2 moveslocationinto Player, dropsPiece, and ends the turn withloc.landedOn(this)(Polymorphism, 4.6).
Monopoly Start Up. MonopolyGame creates the Board and the Players (and the dice or the Cup), and Board creates the 40 Squares, all by Creator. Larman shows this compactly with «create» dependencies in a class diagram.
Every method is either a command, which changes state and returns void, or a query, which returns data and has no side effects, but never both. That is why Die.roll() is void and getFaceValue() is separate (and likewise Cup.roll() and Cup.getTotal()). The benefit is predictable code: a getter never surprises you by changing anything.
Check yourselfThe operation contract for enterItem says: a SalesLineItem instance sli was created; sli was associated with the current Sale; sli.quantity became quantity; sli was associated with a ProductDescription. Which principle handles each postcondition?
Instance creation: Creator (Sale contains line items). The two associations formed: done by the creator, which has visibility (Sale adds sl to lineItems; the description is passed into create(desc, qty)). The attribute modification (quantity): set by the object that owns it, during creation, i.e. Expert.
Check yourselfSpot the error: a student's enterItem diagram has :Register send create(desc, qty) to :SalesLineItem and then addLineItem(sli) to :Sale.
It ignores Creator: the Sale contains the line items, so Sale should create them (via makeLineItem(desc, qty)). Register creating them adds a Register→SalesLineItem dependency (worse Low Coupling) and gives the controller work it should delegate (worse High Cohesion).
Check yourselfWhy is Start Up designed last, and why does Register's constructor take the catalog?
Only after designing the system operations do you know which objects must exist and which permanent visibilities are needed. enterItem needs Register to send getProductDescription to the catalog, so Start Up passes the catalog into create(pc), giving Register attribute visibility to it.
Design each system operation separately: Controller for the found message, Creator and Expert for the contract's postconditions, Low Coupling and High Cohesion to choose between alternatives, Pure Fabrication, Indirection and Polymorphism when Expert backfires. Check visibility, ignore display, design Start Up last, and label each message with its principle.
Course Examples and Workshop Solutions
The lectures and workshops apply the same reasoning to three more systems. They are worth knowing because exam questions are built the same way: a small domain, then "who should do X, and why?".
The Library Borrowing System (lectures, weeks 4–5)
The lectures ask a sequence of design questions about it:
- Who creates a
BorrowedItem?BorrowingRecord, by Creator: it contains (compositely aggregates) them. - Who checks whether a member has reached the borrowing limit?
Member.hasReachedLimit(), by Expert: Member hasborrowLimitand is associated with its records. - Why not
BorrowingKioskorBorrowingRecord? Both are acceptable, but each costs more, as the graphs show.
- The kiosk is the controller; who does the application logic? No domain class fits without hurting High Cohesion or Low Coupling, so use the Pure Fabrication
BorrowingHandler(drawn in 4.7). The kiosk keeps the UI-facingmemberLogin,listItem,returnItemandnewBorrowing, and delegates. - Future: videos as well as books. Apply PV at the known variation point
Book, with a stableIteminterface (getTitle,getDetail,getQuantity) implemented byBookandVideo.getDetailis polymorphic, andBorrowingItemrefers toItem.
The lecture's closing note: several designs can satisfy GRASP, so use Low Coupling and High Cohesion to choose between them.
Workshop 4: online marketplace
The domain has User, Listing, Item, Category, SubCategory, Bid, Transaction, Message, Photo and Address. The partial solution reasons as follows.
| Question | Answer | Principle and reason |
|---|---|---|
| Who finds the highest bidder? | Listing | Expert: it has all the bids and their users |
| Who lists all items in a subcategory? | SubCategory | Expert: most direct access, rather than scanning all Items |
| Who knows a user's past transactions? | User | Expert: knows both its bought and its sold transactions |
Who creates a Bid? | Listing | Creator: closely uses and aggregates bids, and has most of the initialising data. User has the bidder but doesn't use the bid afterwards |
Who creates a Transaction? | Listing | Creator: has all the initialising data (the listing, the buyer, who is the highest bidder, and the seller) and can "finalise itself" |
Listing and Transaction, and there is one obvious creator. Avoid duplicated creation logic and ambiguous control flow.Controller. The controllers offer createListing(...) and makeBid(...), taking all the user input as parameters. Use one controller if Listing owns both behaviours; if the behaviours are split between Listing and User, use two use-case controllers, which keeps cohesion and separates concerns. The workshop's lesson: GRASP principles are not mutually exclusive, and every choice is a trade-off you must justify.
Workshop 5: Mobile Service Terminal (MST) POS
The indicative solution maps onto GRASP like this.
| Design element | GRASP reading |
|---|---|
MST with makeSale(): Sale, scan(barcode): Item, processPayment(cardData): boolean, checkStock, checkSales | A device façade controller, like Register |
MST creates Sale | Creator: it records currentSales |
Sale.calculatePrice() | Expert |
Abstract Card (CreditCard, StoreCard); abstract CardAgency with validate and charge (VisaAgency, StoreCardAgency) | Polymorphism and PV for the card types and the external agencies |
SalesServer, SecurityServer.releaseSale(Sale), StockInventoryServer | Indirections to external systems |
The worksheet stresses iterating: dynamic modelling reveals missing methods and attributes, so you update the DCD to match.
Tony's Pizza (weeks 4–5) is covered under each principle: Creator (4.2), Expert (4.3), the OrderHandler use-case controller (4.5) and the Item.getPrice() polymorphism (4.6).
Check yourselfIn the marketplace, why is Listing a better creator of Bid than User, even though the User is the bidder?
Creator: Listing closely uses and aggregates its bids and has most of the initialising data. The User supplies the bidder but doesn't use the bid afterwards, so making it the creator would add coupling without a containment or usage relationship to justify it.
Check yourselfIn the MST solution, a new card type (say, a gift card) must be supported. Which design elements make that cheap, and which principles are they?
The abstract Card (and, for its external processor, the abstract CardAgency with validate/charge). Adding GiftCard (and a matching agency) is new subclasses with no change to MST or Sale: Polymorphism as the mechanism, Protected Variations as the goal.
Library: BorrowingRecord creates BorrowedItems (Creator), Member checks the limit (Expert; also an Indirection), BorrowingHandler holds the logic (Pure Fabrication), Item protects against videos (PV + Polymorphism). Marketplace: Listing finds the highest bidder and creates Bids and Transactions. MST: a device façade controller, polymorphic cards and agencies, server indirections.
Exam Toolkit exam
This last section turns the Part into exam technique: a cheat sheet for the multiple-choice questions, a template for written justifications, and the mistakes that cost marks.
Decision cheat sheet for MCQs
Match what the question stem describes to the left column. Several rows warn that a more specific GoF pattern beats the nearest GRASP answer.
| If the question describes… | Pick |
|---|---|
| "Who should create X?": a container, aggregate, recorder, or the class with the initialising data | Creator |
| Creation is complex or conditional (choose one of a family of classes, pooling, config-driven) | Factory (GoF), not Creator |
| "Where should this new feature/responsibility go?", "how should he start?", "which class knows X?" | Information Expert |
| A button, GUI event or external request must reach the back end/domain; "first object to receive a system event"; UI must not contain logic | Controller |
| One handler class receives all requests and does too much | Controller (bloated controller → use-case controllers + delegation). If the option is phrased as the problem: Low Cohesion |
| A class with many unrelated methods, a "dumping ground", hard to understand/maintain/reuse | Low Cohesion (the problem) / High Cohesion (the principle) |
| Changes ripple to many classes; too many dependencies; "reduce impact of change"; evaluating two designs | Low Coupling |
| Behaviour differs by type or by the role an object plays; replace if/switch on type; pluggable alternatives | Polymorphism (the official answer to 2021 S2 Q1.1, a person whose behaviour changes with their role). Prefer Strategy only if the stem describes interchangeable algorithms for one context object, State if behaviour changes with state |
| Need a helper/service class that is not a domain concept to keep domain classes clean (persistence, dice cup, generic service) | Pure Fabrication |
| Put an intermediary between A and B so they are not directly coupled | Indirection, unless a more specific GoF intermediary fits: Adapter (different interfaces), Façade (simplify a subsystem), Observer (event notification) |
| Protect the system from a known or likely change; "stable interface"; future third-party systems | Protected Variations |
| One simple entry point to a complex existing subsystem | Façade (GoF), not Controller |
| Several external systems with different interfaces | Adapter (GoF); in GRASP terms Indirection + Polymorphism + PV |
The tie-break rule
The options mix GRASP and GoF names, so decide first which family the stem is about.
Past-exam MCQs. All 20 items from 2021 are worked in Part IX. The GRASP ones are S1 Q1.1 (Low Cohesion), S2 Q1.1 (Polymorphism, per the official answer), S2 Q1.2 (Controller), S2 Q1.5 (Information Expert) and S2 Q1.10 (Controller).
How to justify a design choice in writing
A justification earns marks when it names the principle and gives specific evidence. Build each one from these parts.
Model justifications in that style:
- "By Information Expert,
Saleis responsible forgetTotal()because it aggregates all itsSalesLineItems, the information needed to sum the subtotals. EachSalesLineItemcomputesgetSubtotal()as the expert on its quantity and itsProductDescription." - "By Creator,
Boardcreates theSquares because it compositely aggregates them. No coupling is added, since Board must reference the Squares anyway." - "By Controller,
MonopolyGame(a façade controller representing the root object) receivesplayGamefrom the UI. This is acceptable because there are only two system operations, so it will not become bloated. It delegates turns toPlayer, keeping High Cohesion." - "
Salerather thanRegistercreatesPayment(Creator: Sale closely uses Payment). This keeps coupling low, because Register need not know Payment, and keeps Register cohesive." - "
PersistentStorageis a Pure Fabrication. Saving belongs to no domain concept; putting it inSale(as Expert suggests) would make Sale incohesive and coupled to JDBC. The fabrication is cohesive and reusable, and acts as an Indirection between domain and database." - "
ITaxCalculatorAdapterapplies Protected Variations at the known variation point of external tax-calculator APIs. Adapters give Indirection, andgetTaxesis polymorphic, so a new calculator means adding one class without changingSale(OCP)."
On diagrams, label messages and classes with the principle ("by Creator", "by Expert", "by Polymorphism"), as Larman and the lecturers do. The project rubrics (written by the same teaching team) reward "correct identification of design concerns using GRASP" and justification with specific references to classes, methods and responsibilities. They also say that a well-justified design based on correct principles is sufficient: it need not be "the best". Vague "this is good design" statements earn nothing.
Common mistakes
- Assigning responsibilities the way people do the job. A
Cashierobject that does everything, orPlayer.takeTurnjustified by "humans take turns". Justify by Expert, coupling and cohesion instead. - Confusing domain and design classes. Creator and Expert apply to software classes. The domain model is inspiration, not a 1:1 template: classes can be added (fabrications), removed (
Piece), or have different multiplicities. - UI objects handling system events. A window should not send
Sale.makeLineItemitself, and the MVC/UI controller is not the GRASP controller. - Controllers doing the work. Business logic inside
RegisterorOrderHandler, or one façade controller for dozens of system events. - Testing types with if/switch. Use polymorphic operations. In code the corollary forbids
instanceofdispatch; analtframe in a diagram only documents the polymorphic cases. - Using Pure Fabrication as an excuse for new classes. "Manager" classes that take all the behaviour away from the data and leave anaemic domain objects.
- Speculative generality. Interfaces and polymorphism at points that will never vary.
- Treating Low Coupling as "no coupling", or worrying about coupling to stable library classes.
- Duplicated or ambiguous creation. Two classes able to create the same object, such as the buyer and the seller
Usercreating aTransaction. - Expert applied blindly to technical services. Domain classes saving themselves to the DB, or talking TCP to external systems.
- Calling Controller and Façade the same thing in an MCQ. A façade controller handles system events from the UI; GoF Façade simplifies access to a subsystem.
- Treating information hiding as data encapsulation. Parnas meant hiding design decisions that are likely to change, which is PV.
- Unjustified answers. Naming a principle without the evidence (attribute, association, aggregation), or without saying what was traded off.
Check yourself"A developer needs to add a 'loyalty points' calculation to the POS. Where should he start?"
- Controller
- Information Expert
- Indirection
- Pure Fabrication
B, Information Expert. "Where should this new responsibility go / how should he start?" is the Expert cue: find the class that has the information needed (e.g. the Sale's total and the customer's history) and give it the job.
Check yourself"An application must work with three payment gateways that each expose a different API, and more may be added later. Which is the best answer?"
- Indirection
- Controller
- Adapter
- High Cohesion
C, Adapter. Several external systems with different interfaces is the specific GoF structure, so the tie-break rule picks the GoF pattern. In GRASP terms it is Indirection + Polymorphism + PV, which is why Indirection is a tempting distractor.
Check yourselfImprove this exam justification: "Sale creates Payment because it is good design and follows GRASP."
It names no principle and gives no evidence. Better: "By Creator, Sale creates Payment because Sale closely uses its Payment and has the initialising data (it knows the total). This supports Low Coupling: Register never needs to know Payment. The alternative, Register (which records payments), was rejected because it adds a Register→Payment dependency and makes Register less cohesive."
Check yourself"The system must give clients a single simple entry point to a complex legacy billing subsystem." Controller or Façade?
Façade (GoF). One simple entry point to a complex existing subsystem is Façade's structure. A (façade) controller is about receiving system events from the UI layer; Controller is a distractor here.
- Match the stem's cue to a principle; if it describes a specific GoF structure, pick the GoF pattern.
- Justify with principle + specific evidence + trade-off, and label diagram messages "by X".
- Avoid the classic traps: human-role reasoning, UI handling events, controllers doing work, type switches, gratuitous fabrications and speculative interfaces.