skip to content

What does the GRASP "Creator" principle say about deciding which class should be responsible for creating instances of another class?

level: juniorimportance: must knowfreq 55%

answer

  1. aggregates / contains / records / closely uses / has init data
  2. creation adds no NEW coupling edge
  3. Low Coupling applied to instantiation
  4. initializing data = strongest tie-break
  5. complex construction → Factory / Pure Fabrication

basics

~20 s

Creator says: give the job of making a new object to a class that already aggregates, contains, records, closely uses, or holds the data needed to build it. That class already knows about it, so creating it adds no new coupling.

solid answer

~50 s

GRASP (General Responsibility Assignment Software Patterns) is a set of nine heuristics for deciding which class gets which responsibility. "Creator" answers one specific question: who should instantiate class A? Assign creation of A to class B if B aggregates A objects, contains A objects, records A instances, closely uses A, or has the initializing data that A's constructor requires. The motivation is coupling: B already has to know about A for one of those reasons, so making B the creator introduces no *new* dependency edge in the design. If several classes qualify, prefer the one that aggregates or contains A (it owns A's lifetime), and, where that is ambiguous, the one holding the initializing data. Creator is a default, not a law — when construction is complex, conditional, or must vary by configuration, delegate to a Factory Method, Abstract Factory, Builder, or a Pure Fabrication factory class instead.

code

pseudocode · 15 lines
pseudocode
// Creator: Order aggregates + contains OrderLine, and holds the collection
class Order {
  private lines = []

  addLine(product, quantity) {          // Order is the Creator
    line = new OrderLine(product, quantity)
    lines.add(line)
    recalculateTotal()                   // invariant stays enforceable
    return line
  }
}

// Anti-pattern: an outside class builds the part and pokes it in
line = new OrderLine(product, qty)       // controller now depends on OrderLine
order.getLines().add(line)               // internals leaked, total not updated

go deeper

for a junior

State the rule and one criterion with an example: Order creates OrderLine because it contains them. Mention that it keeps coupling low.

for a middle

List all five criteria, explain the coupling rationale, and name the tie-break (aggregation/ownership, then initializing data). Give a concrete before/after.

for a senior

Frame Creator as Low Coupling applied to instantiation, relate it to Information Expert and Pure Fabrication, and state explicitly when to override it with a Factory/Builder or DI.

for a principal

Discuss creation as lifecycle ownership: aggregate roots, invariant enforcement at the creation boundary, reconstitution vs creation in ORMs, and where the composition root ends and domain creation begins.

## What GRASP is **GRASP** stands for *General Responsibility Assignment Software Patterns* (Craig Larman, *Applying UML and Patterns*). It is a set of nine naming-and-reasoning heuristics — Information Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection, Protected Variations — for the most basic activity in object design: **deciding which class should hold which responsibility**. They are not code patterns like Singleton or Observer; they are decision rules you apply *before* you reach for such patterns. A **responsibility** here means an obligation of an object: either *doing* something (compute, create, coordinate) or *knowing* something (its own data, related objects, derivable values). ## The Creator heuristic, precisely > **Problem:** Who should be responsible for creating a new instance of some class `A`? > > **Solution:** Assign that responsibility to a class `B` if **one or more** of the following is true: > 1. `B` **aggregates** `A` objects (whole-part with ownership), > 2. `B` **contains** `A` objects (holds them in a collection/field), > 3. `B` **records** instances of `A` (keeps a registry/log of them), > 4. `B` **closely uses** `A` objects, > 5. `B` **has the initializing data** that will be passed to `A` at construction (`B` is an *Information Expert* with respect to `A`'s construction). > > Rule 5 is the strongest tie-breaker when the others conflict; rules 1–2 (ownership of lifetime) are usually preferred when several classes are otherwise equal. ### Why this rule and not something else Every `new A(...)` in class `B` creates a **compile-time dependency** from `B` to `A`. Design quality is largely about keeping the dependency graph small and acyclic. Creator's insight is: *if `B` already depends on `A` anyway* (because it stores it, owns it, or uses it), then adding "and `B` builds it" costs **zero new edges**. Handing creation to some unrelated class `C` would add a brand-new edge `C → A` for no benefit. So Creator is really **Low Coupling applied to instantiation**. A second benefit is **lifecycle clarity**: the object that creates a part is usually the object that can decide when the part dies, is replaced, or must be kept consistent. Ownership and creation travelling together makes invariants enforceable. ## Concrete illustration (language-neutral) Domain: an `Order` holds many `OrderLine`s; each line references a `Product` and a quantity. - Who creates `OrderLine`? **`Order`** — it aggregates and contains lines, and it holds the collection the new line must join. So `Order.addLine(product, qty)` internally constructs the line. - If a UI or controller did `new OrderLine(...)` and then `order.lines.add(line)`, the controller gains a dependency on `OrderLine`, `Order` must expose its internal collection, and `Order` can no longer guarantee invariants such as "no duplicate product lines" or "total must be recomputed". ## The five criteria, one at a time | Criterion | Meaning | Example | |---|---|---| | aggregates | whole–part with ownership; part dies with whole | `Order` → `OrderLine`, `Document` → `Paragraph` | | contains | holds in a field/collection (weaker than aggregation) | `Playlist` → `TrackEntry` | | records | keeps a log/registry of instances it did not necessarily own | `AuditLog` → `AuditEntry`, `Board` → `Move` | | closely uses | calls it heavily, works with it intimately | `HttpClient` → `Request` | | has initializing data | already knows every value the constructor needs | `Payment` knows amount + currency → creates `Money` | ## Trade-offs and when Creator is the wrong answer Creator is a **default heuristic**, applied when creation is simple. Deviate when: 1. **Creation is complex or conditional.** Choosing among subclasses, reading configuration, applying caching/pooling, or long multi-step assembly. Then use a **Factory Method**, **Abstract Factory**, **Builder**, or a dedicated factory class (**Pure Fabrication** — a made-up class with no domain counterpart, invented to preserve cohesion). 2. **Creation would drag heavy dependencies in.** If constructing `A` needs a database connection, a clock, or a remote service, letting the domain `B` create it pollutes `B` with infrastructure. Inject an abstraction instead. 3. **You want to vary the implementation** (test doubles, plugins, feature-flagged variants). Hard-coded `new` is not substitutable, so hand construction to a factory or a dependency-injection **composition root** (the single place near the program's entry point where the object graph is wired). 4. **The object is long-lived and shared** (services, repositories, connection pools). Creator is about *domain/entity/value* objects; wiring of long-lived collaborators is a container/composition-root concern. ## Relation to the other GRASP heuristics - **Information Expert** ("assign a responsibility to the class that has the information needed to fulfil it") is the parent idea; criterion 5 is literally Information Expert applied to construction. - **Low Coupling / High Cohesion** are the evaluative principles Creator optimises for; if following Creator visibly *worsens* them (e.g. the domain class becomes a giant assembler), the evaluative principles win. - **Pure Fabrication** is the standard escape hatch: invent `OrderFactory` when neither `Order` nor anything else in the domain should own the messy construction. ## Common edge cases - **Deserialization / ORM materialisation.** Frameworks reconstruct objects by reflection, bypassing your Creator. That is *reconstitution*, not *creation*; keep a separate path (e.g. a package-private constructor) and don't let it weaken the invariants enforced on the real creation path. - **Circularity.** If `A` creates `B` and `B` creates `A`, you have a cycle; usually a third class or a parameter object should own creation. - **Immutability.** With immutable objects, "modifying" means creating a replacement; the Creator is then also the class that must rebuild and re-store it. - **Static factory methods on the created class itself** (`Money.ofMinorUnits(...)`) do not violate Creator; they are a naming/validation convenience, and the *caller* is still chosen by Creator.

  • If two different classes both satisfy Creator criteria for the same class, how do you choose?
    Prefer the class that aggregates or contains the instance, because it owns the object's lifetime and can enforce invariants. If that is still ambiguous, pick the class that already holds the initializing data — that is the Information Expert criterion and it avoids passing data around just to construct.
  • Does using a dependency-injection container mean Creator no longer applies?
    No. Containers wire long-lived collaborators (services, repositories) at the composition root. Creator governs short-lived domain objects — entities, value objects, DTOs, events — which are created per operation from runtime data and should not come from a container.
  • Is a static factory method like Money.of(amount, currency) a violation of Creator?
    No. It changes only how construction is expressed and validated on the created type; Creator still decides which collaborating class should call it.

A restaurant kitchen makes the plate of food, not the customer — the kitchen already owns the ingredients, the recipe and the plates. Asking the customer to assemble the dish would force them to learn everything the kitchen already knows.

context