The idea
What it is
Once you've decided what classes exist, the harder question begins: which class should do each job? Should the Order compute its own total, or should a Controller reach in and add up the line items? Should the UI window create a new OrderLine, or should the Order make it? GRASP — General Responsibility Assignment Software Patterns, from Craig Larman's Applying UML and Patterns — is a named checklist of nine principles for answering exactly this: where should each responsibility live?
Think of running a team. A new task comes in — "how much is this order worth?" You don't hand it to whoever is standing closest; you hand it to the person who has the information to answer it. The accountant has the numbers, so the accountant tallies the bill. The warehouse has the stock, so the warehouse reports inventory. Assigning work to whoever holds the relevant facts (and the authority to act on them) keeps everyone focused and keeps hand-offs thin. GRASP is that instinct, made explicit for classes.
The one sentence to remember
Give each responsibility to the class that already has the information to fulfill it — that's Information Expert, the heart of GRASP, and most of the other principles are just refinements of it.
Mechanics
How it works
The running example: an e-commerce Order
We'll use Larman's classic point-of-sale shape. An Order contains a list of OrderLine objects; each OrderLine references a Product and a quantity. There's a UI Window where the cashier clicks, and somewhere a Checkout use case fires. The question every principle answers is the same: given a new job, which of these classes should own it?
Information Expert — give the job to whoever has the data
The most-used GRASP principle: assign a responsibility to the class that has the information needed to fulfill it. Who should compute the order's grand total? The Order holds the list of lines, so the Order is the expert — it sums its lines. But each line's subtotal depends on a price and a quantity that live on the OrderLine, so each OrderLine computes its own subtotal. The work flows to where the data already is, and no one has to reach across object boundaries to peek at someone else's internals.
// ❌ BAD: the Controller is NOT the information expert.
// It reaches into the order's internals, then into each line's internals.
class CheckoutController {
grandTotal(order: Order): number {
let total = 0;
for (const line of order.lines) { // poking at Order's internals
total += line.product.price * line.qty; // poking at OrderLine's internals
}
return total; // logic that depends on Order/OrderLine data lives OUTSIDE them
}
}
// ✅ GOOD: Information Expert — the data owner computes its own answer.
class OrderLine {
constructor(private product: Product, private qty: number) {}
subtotal(): number { return this.product.price * this.qty; } // it has the data
}
class Order {
private lines: OrderLine[] = [];
total(): number { // Order has the lines…
return this.lines.reduce((sum, l) => sum + l.subtotal(), 0); // …so Order sums them
}
}Notice the payoff: in the good version, change how a line is priced (add a discount, a tax band) and you edit OrderLine only — the Order and the controller don't move. In the bad version, that pricing logic leaked into the controller, so a pricing change drags the controller along with it. Information Expert produces low coupling and high cohesion as a side effect — which is exactly why it's the workhorse.
Creator — who should make a new object?
When something needs to be instantiated, Creator says: assign creation to the class that contains, aggregates, records, or closely uses the new object — and that has the data to initialize it. Who creates a new OrderLine? The Order does: it contains the lines, and it has (or is handed) the Product and quantity needed to build one. The UI Window shouldn't new OrderLine(...) and then hand it over; that scatters construction logic and couples the UI to the line's constructor.
class Order {
private lines: OrderLine[] = [];
// Creator: the container that aggregates OrderLines is the one that makes them.
addLine(product: Product, qty: number): void {
this.lines.push(new OrderLine(product, qty)); // Order owns its parts
}
}Controller — who receives a system event?
When a system event arrives from the outside (the cashier clicks checkout), Controller says: route it to a non-UI object that represents either the overall use case (a CheckoutHandler, a use-case controller) or the system/root object (a façade controller). The point is to keep the UI thin — the window captures the click and immediately delegates — so business logic never gets tangled into buttons and views. A bloated controller that does everything, though, is an anti-pattern; the controller should coordinate, then delegate the real work to the experts.
Low Coupling & High Cohesion — the evaluative pair
These two aren't about a specific job — they're the scorecard you hold up to any candidate assignment. Low Coupling: prefer the assignment that creates the fewest, thinnest dependencies between classes. High Cohesion: prefer the assignment that keeps each class focused on one set of related responsibilities. When two placements both seem plausible, pick the one that keeps both dials healthy. Information Expert usually wins on this scorecard automatically — putting the logic next to its data is what keeps coupling low and cohesion high.
GRASP sits underneath SOLID and the GoF patterns
These principles aren't a competing system — they're the foundation. High Cohesion restated is the Single Responsibility Principle; Protected Variations is the spirit behind the Open/Closed and Dependency Inversion principles; Polymorphism and Indirection are the seeds of half the Gang of Four patterns (Strategy, Adapter, Mediator, Facade). Learn GRASP and the rest of OO design starts to feel like named special cases of it.
The remaining five, in one line each
- Polymorphism — when behavior varies by type, assign the variation to subtypes via a polymorphic operation instead of
if/else/switchon a type tag (e.g. eachPaymentMethodcharges itself). - Indirection — assign a responsibility to an intermediate object so two things don't couple directly (a mediator/adapter sitting between them).
- Pure Fabrication — when no domain class is a good home, invent a made-up class that doesn't represent a real-world concept (e.g. a
OrderRepositoryorTaxService) to keep coupling low and cohesion high. - Protected Variations — wrap an unstable point (a third-party API, a changing rule) behind a stable interface so variation on one side can't ripple to the other.
- Indirection + Pure Fabrication + Protected Variations together are how you tame the messy parts the pure domain model can't absorb cleanly.
These are heuristics, not laws
GRASP names forces to weigh, not rules to obey mechanically. Don't invent a Pure Fabrication for every responsibility — a OrderTotalCalculatorServiceManager that just re-derives what Order already knows is worse than Order.total(). Reach for fabrication only when the domain class genuinely isn't the right home (persistence, cross-cutting concerns). When Information Expert gives a clean answer, take it.
Interactive prototype
See it. Build it. Break it.
A sandboxed, hands-on simulation — no setup, no install. Play with it as you read.
About this simulation
A round-by-round responsibility quiz. Each scenario asks "who should own this job?" — compute the order total, create an order line, receive the checkout event — and shows you the candidate classes with their fields so you can see who actually holds the information. Click the class you'd assign it to; the console narrates the governing GRASP principle and the coupling/cohesion cost of a wrong pick. Step through five scenarios with a running score, then Reset to try again.
Hands-on
Try these yourself
Open the prototype above, predict what happens, then verify.
Assign the grand-total responsibility
Scenario 1 asks "who should compute the order's grand total?" and shows four cards — Window, CheckoutController, Order, OrderLine — each listing its fields. Read the fields: only Order holds lines[]. Click Order. The console confirms Information Expert — Order has the line items, so Order sums them, and your score ticks up.
Pick a wrong owner on purpose
On the "who should create a new OrderLine?" scenario, click Window (the UI) instead of Order. You'll get a ✗ and a narration of the cost — the UI now knows OrderLine's constructor; coupling rises, cohesion drops. The feedback names the principle you violated (Creator) and what the right owner was, so a wrong pick still teaches.
Step through all five and Reset
Use Next › and ‹ Prev to walk the full set — grand total, create line, receive checkout, charge-by-type (Polymorphism), and persist the order (Pure Fabrication). The header bar tracks scenario X / 5 and your live score. Hit Reset to clear answers and the log, then try to score 5 / 5 in one pass.
In practice
When to use it — and what trips people up
When to reach for GRASP
Use GRASP whenever you're staring at a method and unsure which class it belongs on — during design, in code review, or mid-refactor. The trigger questions are concrete: Does this logic reach into another object's fields to do its job? (move it to the Information Expert). Is the UI doing business work? (introduce a Controller). Is a class creating something it doesn't contain or use? (revisit Creator). Does no domain class feel like the right home? (consider a Pure Fabrication). GRASP turns the vague "this feels off" into a named force you can argue about.
Don't over-fabricate
The most common GRASP overreach is sprinkling Manager, Service, and Helper classes everywhere under the banner of Pure Fabrication. Each one you add removes logic from the object that owns the data, lowering cohesion of the domain model. Fabricate when there's a real reason (persistence, an external integration, a cross-cutting concern) — not as a reflex.
What it gives you
- A shared vocabulary — "that's not the Information Expert" or "the UI is acting as a Controller" makes design reviews precise instead of hand-wavy.
- Information Expert and Creator give low coupling and high cohesion almost for free — the data and the logic that needs it end up together.
- GRASP underlies SOLID and the GoF patterns, so it transfers everywhere and makes those higher-level ideas click.
- It scales down — useful for a single method's placement, not just whole-system architecture.
Common mistakes
- They're heuristics, not algorithms — two engineers can weigh the forces differently and reach different (both defensible) assignments.
- Overusing Pure Fabrication scatters logic into anemic Manager/Service classes and hollows out the domain model.
- A Controller can swell into a God class if it does work instead of delegating to the experts.
- Knowing the names doesn't replace judgment — you still have to read the data flow to see who the real expert is.
Reference
Code & further reading
A minimal reference implementation and pointers worth bookmarking.
// Information Expert + Creator: the Order owns its lines and the totalling logic.
class Product {
constructor(public name: string, public price: number) {}
}
class OrderLine {
constructor(private product: Product, private qty: number) {}
// Information Expert: the line has the price and qty, so the line computes subtotal.
subtotal(): number { return this.product.price * this.qty; }
}
class Order {
private lines: OrderLine[] = [];
// Creator: Order CONTAINS OrderLines, so Order creates them.
addLine(product: Product, qty: number): void {
this.lines.push(new OrderLine(product, qty));
}
// Information Expert: Order has the lines, so Order sums them.
total(): number {
return this.lines.reduce((sum, line) => sum + line.subtotal(), 0);
}
}
const order = new Order();
order.addLine(new Product("Keyboard", 49), 2);
order.addLine(new Product("Mouse", 25), 1);
console.log(order.total()); // 123 — no class reached into another's internalsReferences & further reading
6 sources- Articleen.wikipedia.org
Wikipedia — GRASP (object-oriented design)
The canonical reference list of all nine principles with crisp one-paragraph definitions of each. Start here.
- Book
Applying UML and Patterns — Craig Larman
The book that introduced GRASP and made responsibility assignment the centerpiece of OO design, taught through a running point-of-sale case study. The original source.
- Articlefluentcpp.com
GRASP: 9 Must-Know Design Principles for Code — Fluent C++
A practical walk through all nine with code, and a useful take on how the principles aren't equally fundamental — some are overarching, some are techniques.
- Articlebool.dev
GRASP — General Responsibility Assignment Software Patterns (bool.dev)
A focused explainer that leans on Information Expert and Controller with worked examples — good for cementing the two you'll use most.
- Articleen.wikipedia.org
Wikipedia — Single-responsibility principle
Read alongside High Cohesion: SRP is essentially that GRASP principle restated as "one reason to change."
- Paperhome.cs.colorado.edu
GRASP Patterns — David Duncan (University of Colorado, PDF)
A concise academic lecture deck covering each principle with UML and motivation — handy as a printable revision sheet.
Knowledge check
Did it land?
Quick questions, answers revealed on submit. Sign in to save your best score.
question 01 / 06
An order contains line items. Following Information Expert, which class should compute the order's grand total?
question 02 / 06
By the Creator principle, which class should instantiate a new OrderLine?
question 03 / 06
A 'checkout' system event arrives from the UI. By the Controller principle, what should receive it?
question 04 / 06
What role do Low Coupling and High Cohesion play among the GRASP principles?
question 05 / 06
When is Pure Fabrication the right call?
question 06 / 06
Charging an order can happen by card, wallet, or gift code, each with different logic. Which GRASP principle fits best?
0/6 answered