GRASP (General Responsibility Assignment Software Patterns) is a set of nine responsibility-assignment principles described by Craig Larman. They answer a concrete design question: which object should know, create, coordinate, or vary this behavior?

The principles are heuristics, so they can point in different directions. Information Expert may place behavior beside the data it needs, while Low Coupling may argue against importing a dependency into that object. The design work is choosing which pressure matters at that boundary.

GRASP applies Software Design/Paradigms/OOP encapsulation and polymorphism to responsibility assignment rather than prescribing a fixed class structure.

The Nine Principles

Information Expert assigns a responsibility to the object that already has the information required to perform it. An Order that owns line items can calculate its total without exposing its internals to an OrderCalculator.

Creator places construction with an object that contains, records, closely uses, or has the initialization data for the new object. Order is a natural creator of the line items it owns.

Controller receives a system operation at the boundary and coordinates the use case. It represents the system, a subsystem, or a use-case session rather than performing all domain work itself. An MVC controller can play this role, but the UI pattern and the GRASP principle are not identical.

Low Coupling favors assignments that limit how many elements depend on each other and how much they know. Adding an interface helps only when it creates a stable boundary. An interface with no real variation merely moves the dependency.

High Cohesion keeps an object’s responsibilities strongly related. A controller that validates input, calculates prices, writes SQL, and sends email has absorbed several reasons to change.

Polymorphism assigns type-dependent behavior to the types that vary. It replaces a conditional owned by an outsider when new variants are expected to keep arriving.

Pure Fabrication introduces a non-domain object when no domain concept is a good home for a responsibility. A persistence mapper or email gateway can improve cohesion without pretending to be part of the business model.

Indirection inserts an intermediate object so two elements do not depend on each other directly. The extra hop earns its place only when it absorbs a real variation or communication rule.

Protected Variations puts a stable boundary around an identified point of change. A payment-provider contract protects domain code when providers genuinely vary. It is speculative overhead when there is one fixed integration with no separate policy.

Example: Applying Information Expert

// BAD: OrderService calculates total by reaching into Order's data
public class OrderService
{
    public decimal CalculateTotal(Order order) =>
        order.LineItems.Sum(li => li.Price * li.Quantity);
}
 
// GOOD: Order calculates its own total (Information Expert)
public sealed class Order
{
    private readonly List<LineItem> _lineItems = new();
 
    public decimal Total => _lineItems.Sum(li => li.Price * li.Quantity);
}

Order owns both the data and the invariant, so the calculation can remain inside its boundary. A separate service becomes justified when the result depends on information or policy outside that boundary.

Pitfalls

Treating Information Expert as an Absolute

Putting every calculation on the object that holds the data can produce a domain object that also performs I/O, coordinates other aggregates, or imports infrastructure concerns. Information Expert identifies a strong candidate, not an automatic winner. Keep behavior on Order when it depends on Order state. Move cross-aggregate policy or external communication behind a separate collaborator.

Tradeoffs

DecisionOption AOption BWhen AWhen B
Information Expert vs Pure FabricationPut behavior on the class that has the dataCreate a service class for the behaviorThe rule depends on state and invariants already owned by the objectThe work coordinates aggregates or external I/O that does not belong in the domain object
Low Coupling vs Information ExpertMinimize dependencies via indirectionAssign to the class with the data even if it adds a dependencyWhen the dependency would cross module or layer boundariesWhen the dependency is within the same aggregate and adding indirection adds complexity without benefit
GRASP vs GoF PatternsGRASP heuristics for who should own thisGoF patterns for how the collaboration worksAssigning responsibilityNaming a recurring construction, composition, or communication structure

The better assignment keeps the change-prone rule close to the information it needs without pulling unstable dependencies across a boundary. No single GRASP principle settles every case.

Example: Polymorphism Replacing Conditionals

// BAD: type-switch violates Polymorphism principle
public string GenerateReport(string type, ReportData data) => type switch
{
    "pdf" => GeneratePdf(data),
    "csv" => GenerateCsv(data),
    _ => throw new ArgumentException($"Unknown type: {type}")
};
 
// GOOD: Polymorphism — each type owns its generation logic
public interface IReportGenerator
{
    string Generate(ReportData data);
}
 
public sealed class PdfReportGenerator : IReportGenerator
{
    public string Generate(ReportData data) => /* PDF logic */ string.Empty;
}
 
public sealed class CsvReportGenerator : IReportGenerator
{
    public string Generate(ReportData data) => /* CSV logic */ string.Empty;
}
 
// Adding a new format = new class, no changes to existing code (Open/Closed)

Questions

References