GRASP (Patrones de software de asignación de responsabilidad general) es un conjunto de nueve principios introducidos por Craig Larman en 'Aplicación de UML y patrones' (1997). Mientras SOLID le dice cómo estructurar las relaciones de clases individuales, GRASP aborda una pregunta más fundamental que todo desarrollador enfrenta en el momento del diseño: ¿qué objeto debería ser responsable de este comportamiento? GRASP proporciona heurísticas con nombre para responder esa pregunta de forma sistemática en lugar de por intuición. Los patrones no son algoritmos: son lentes que hacen que el proceso de razonamiento sea explícito y comunicable.

Los patrones GRASP son heurísticas de diseño, no reglas estrictas. Varios patrones trabajan juntos en la misma decisión de diseño; por ejemplo, un Controlador satisface los patrones Controlador y Creador simultáneamente. El valor de GRASP no está en aplicar cada patrón de forma aislada, sino en tener vocabulario compartido para las discusiones sobre asignación de responsabilidades.
Los nueve patrones GRASP de un vistazo
Haga clic en cada tarjeta para obtener una descripción general rápida; a continuación se muestran ejemplos detallados
Experto en información: el patrón más utilizado

Information Expert es el patrón GRASP aplicado con más frecuencia y la fuente de la pregunta de diseño más común: "¿Dónde debería colocar este método?" La respuesta: en la clase que tiene los datos que necesita el método. Si calcular el total de un pedido requiere conocer los artículos en línea, la clase de Pedido (que contiene los artículos) debe calcular su propio total, no un servicio externo que llega al interior del Pedido para extraer los artículos.

Haga clic en las líneas resaltadas para ver que Information Expert se aplicó correctamente y se violó.
java
1
// ── VIOLATION: behavior separated from the data it needs ────────
2
class OrderPricingService {
3
  Money calculateTotal(Order order) {
4
    return order.getItems().stream()                      // reaching into Order
5
      .map(i -> i.getPrice().multiply(i.getQuantity()))   // using Order's data
6
      .reduce(Money.ZERO, Money::add);
7
  }
8
}
9
10
// ── FIX: assign responsibility to the class with the information ─
11
class Order {
12
  private final List<OrderLineItem> items;
13
14
  Money total() {
15
    return items.stream()
16
      .map(OrderLineItem::subtotal)  // delegate to each item's expert
17
      .reduce(Money.ZERO, Money::add);
18
  }
19
}
20
21
class OrderLineItem {
22
  private final Money price;
23
  private final int quantity;
24
  Money subtotal() { return price.multiply(quantity); }  // expert for its own data
25
}
Controlador: separación de eventos del sistema de la lógica del dominio

El patrón Controlador responde: ¿quién debería manejar un evento del sistema (una acción del usuario, un mensaje entrante, un desencadenante programado)? No los objetos de dominio: deberían ignorar los mecanismos de entrega. No la interfaz de usuario: debería ignorar la lógica empresarial. El Controlador es una clase que no es UI y que recibe eventos y los delega en objetos de dominio. Coordina pero no calcula. En la práctica: un controlador REST es un controlador GRASP. También lo es un consumidor de mensajes, un controlador de comandos CLI o un programador de trabajos por lotes: todos son controladores para diferentes mecanismos de entrega.

Haga clic en las líneas resaltadas para ver el patrón del Controlador y sus límites
java
1
// ── GRASP Controller: delegates, does not compute ───────────────
2
@RestController
3
class PlaceOrderController {
4
  private final PlaceOrderService service;
5
6
  @PostMapping("/orders")
7
  ResponseEntity<OrderResponse> place(@Valid @RequestBody PlaceOrderRequest req) {
8
    // Controller responsibilities: receive, validate format, map, delegate
9
    PlaceOrderCommand cmd = req.toCommand();     // map input
10
    Order order = service.placeOrder(cmd);       // delegate ALL logic
11
    return ResponseEntity.ok(OrderResponse.from(order)); // map output
12
    // NO business logic here — zero domain rules in the controller
13
  }
14
}
15
16
// ── Same domain, different controller (Kafka consumer) ──────────
17
@KafkaListener(topics = "order-requests")
18
class PlaceOrderEventController {
19
  private final PlaceOrderService service;
20
  void handle(PlaceOrderEvent event) {
21
    service.placeOrder(event.toCommand());  // same service, different entry point
22
  }
23
}
Polimorfismo: sustitución de condicionales por tipos

El patrón Polimorfismo aborda la variación de comportamiento basada en tipos. Cuando ve un interruptor o if/else en un campo de tipo, generalmente es una señal de que se aplica el patrón de polimorfismo: cada rama del condicional debe ser un subtipo, y el comportamiento debe ser enviado por el sistema de tipos, no por la persona que llama. Esto está directamente relacionado con OCP: el switch crece con cada nuevo tipo; El polimorfismo significa agregar un nuevo tipo y agregar una nueva clase sin tocar el código existente.

Haga clic en las líneas resaltadas para ver cómo el polimorfismo reemplaza un cambio de tipo
java
1
// ── VIOLATION: type switch that grows with every new discount ────
2
Money applyDiscount(Order order, DiscountType type) {
3
  return switch (type) {
4
    case PERCENTAGE -> order.total().multiply(0.9);
5
    case FIXED      -> order.total().subtract(Money.of(10));
6
    case BOGO       -> order.total().multiply(0.5);  // added later — modified method
7
  };
8
}
9
10
// ── FIX: each type encapsulates its own behavior ─────────────────
11
interface DiscountPolicy { Money apply(Money total); }
12
13
record PercentageDiscount(double rate) implements DiscountPolicy {
14
  public Money apply(Money total) { return total.multiply(1 - rate); }
15
}
16
record FixedDiscount(Money amount) implements DiscountPolicy {
17
  public Money apply(Money total) { return total.subtract(amount); }
18
}
19
record BogoDiscount() implements DiscountPolicy {
20
  public Money apply(Money total) { return total.multiply(0.5); }
21
}
22
23
// Caller — never changes when new discount types are added:
24
Money discounted = policy.apply(order.total());
Variaciones protegidas: la raíz de todos los patrones

Larman llamó a las variaciones protegidas "el principio más fundamental en el diseño de software", y es difícil argumentar lo contrario. Cada patrón de diseño, cada estilo arquitectónico, cada abstracción existe para proteger el código estable de cambios volátiles. La aplicación práctica: identifique los puntos de inestabilidad en su sistema (API externas, reglas comerciales que cambian con frecuencia, tecnología de bases de datos, bibliotecas de terceros) y envuelva cada uno en una interfaz estable. El código que depende de la interfaz está protegido: no cambiará cuando cambie la inestabilidad.

Haga clic en cada tarjeta para ver una aplicación de variaciones protegidas en un sistema real
Fabricación pura: cuando los objetos de dominio no son suficientes

Pure Fabrication resuelve la tensión entre la alta cohesión y la necesidad de trasladar los problemas de infraestructura a alguna parte. Considere la persistencia: la entidad Orden no debe saber cómo guardarse en una base de datos; eso violaría el SRP e introduciría infraestructura en el dominio. Pero la lógica de la persistencia debe vivir en alguna parte. Un repositorio es una pura fabricación: una clase inventada con fines de diseño que no tiene una contraparte directa en el dominio del problema. Existe para darle al comportamiento de persistencia un hogar de alta cohesión y bajo acoplamiento. Otros ejemplos: Mapper, Logger, EventPublisher, CacheManager. Todas son puras fabricaciones, inventadas para el diseño, no para el dominio.

Los patrones GRASP se asignan a los patrones de diseño GoF: la conexión hace que ambos sean más claros
Patrón de AGARREPatrones GoF correspondientesQué hace en la práctica
Experto en información- (principio, no un patrón)El método vive en la clase con los datos.
CreadorMétodo de fábrica, fábrica abstractaCreación de objetos cerca del contexto de uso.
ControladorFachada, ComandoPunto de entrada único para eventos del sistema
Acoplamiento bajoFachada, Mediador, AdaptadorReduce la superficie de dependencia entre módulos.
Alta cohesión- (principio, no un patrón)Comportamiento relacionado en una clase.
PolimorfismoEstrategia, Estado, ComandoEnvío basado en tipos en lugar de condicionales
Fabricación puraRepositorio, Mapeador, RegistradorClase inventada para el comportamiento de la infraestructura.
IndirecciónAdaptador, Proxy, Decorador, PuenteEl objeto intermedio absorbe el acoplamiento.
Variaciones protegidasTodos los patrones: esta es la raízInterfaz estable alrededor del punto de cambio volátil
Conclusión clave: GRASP proporciona nueve heurísticas con nombre para las decisiones de asignación de responsabilidades más comunes en el diseño orientado a objetos. Information Expert mantiene el comportamiento cercano a los datos. El controlador separa la entrega del dominio. El polimorfismo reemplaza los condicionales con tipos. Pure Fabrication brinda a la infraestructura un hogar limpio. Protected Variations es el principio unificador detrás de cada abstracción y cada patrón de diseño.
Qué sigue: SOLID y GRASP rigen cómo se asignan las responsabilidades dentro y entre clases. La siguiente lección se remonta a tres heurísticas transversales (KISS, DRY y YAGNI) que gobiernan el presupuesto de complejidad general de un diseño y explica el principio de mínima sorpresa que las une.