📐 Capítulo 07: Padrões GRASP Fundamentais
Módulo 10: Paradigmas de Programação & Padrões de Projeto • Bloco 2: SOLID e GRASP
🏛️ A Arte da Atribuição de Responsabilidades: Enquanto os padrões GoF catalogam designs e estruturas consagradas, os padrões GRASP (General Responsibility Assignment Software Patterns), formulados por Craig Larman, fornecem a bússola mental para responder à pergunta mais crítica do design orientado a objetos: “Qual classe deve ser responsável por fazer o quê?” Neste capítulo, analisamos os cinco padrões fundamentais: Information Expert, Creator, Controller, Low Coupling e High Cohesion.
🗺️ Mapa Conceitual do Capítulo
flowchart TD
GRASP["Padrões GRASP Fundamentais (Craig Larman)"]
GRASP --> EXPERT["1. Information Expert<br/>• Atribua a responsabilidade a quem possui a informação<br/>• Preserva encapsulamento & Elimina Feature Envy"]
GRASP --> CREATOR["2. Creator (Criador)<br/>• B instancia A se B agrega, contém ou usa intimamente A<br/>• Encapsulamento da fábrica inicial"]
GRASP --> CTRL["3. Controller (Controlador)<br/>• Primeiro objeto fora da UI que recebe a operação<br/>• Use-Case Controller vs Facade Controller<br/>• Prevenção do Bloated Controller"]
GRASP --> EVAL["4. Critérios Avaliativos Centrais<br/>• Low Coupling: Minimização de dependências cruzadas<br/>• High Cohesion: Métodos e atributos estritamente focados"]
GRASP --> SYN["5. Sinergia GRASP e SOLID<br/>• Expert & SRP: Coesão máxima de responsabilidade<br/>• Low Coupling & DIP: Dependência mínima e estável"]
style GRASP fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style EXPERT fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px
style CREATOR fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px
style CTRL fill:#fce4ec,stroke:#e91e63,stroke-width:1.5px
style EVAL fill:#ede7f6,stroke:#673ab7,stroke-width:1.5px
style SYN fill:#fffde7,stroke:#fbc02d,stroke-width:1.5px
🏛️ 1. Gênese Teórica: Responsabilidade como Unidade Básica de Design
Na obra seminal Applying UML and Patterns (1997), Craig Larman percebeu que a maior dificuldade enfrentada por engenheiros de software ao modelar sistemas não era a sintaxe da linguagem nem os diagramas da UML, mas a tomada de decisão heurística sobre atribuição de responsabilidades:
- Uma responsabilidade é um contrato ou obrigação conceitual de um tipo de dado. Ela divide-se em duas categorias primárias:
- Responsabilidades de Saber (Knowing): Conhecer dados de encapsulamento privado, conhecer objetos relacionados ou conhecer coisas que podem ser derivadas ou calculadas.
- Responsabilidades de Fazer (Doing): Realizar uma ação sobre si mesmo (ex.: calcular um imposto), iniciar uma ação em outros objetos ou controlar e coordenar atividades entre múltiplos colaboradores.
Os padrões GRASP não são receitas de código prontas (como o Singleton ou Observer do GoF), mas sim princípios cognitivos de raciocínio arquitetural que guiam o desenvolvedor antes e durante a codificação.
1.1 Cartões CRC (Class-Responsibility-Collaboration)
Antes de escrever código ou diagramas detalhados, o design orientado a objetos tradicional consagrado por Ward Cunningham e Kent Beck utiliza os Cartões CRC:
- Classe: O nome da abstração substantiva.
- Responsabilidade: O que a classe sabe e o que a classe faz (frases concisas em linguagem de negócio).
- Colaboradores: Quais outras classes são consultadas ou notificadas para cumprir essas obrigações.
💡 2. Information Expert (Especialista na Informação)
“Assign a responsibility to the class that has the information necessary to fulfill it.” — Craig Larman
2.1 A Heurística Central
O Information Expert é a diretriz mais básica e intuitiva da Programação Orientada a Objetos: coloque o método na classe que detém os atributos necessários para realizar a operação.
- Exemplo Clássico: Em um sistema de ponto de venda (PDV), quem deve ser responsável por calcular o total final da venda?
- Para calcular o total de uma venda, precisamos dos subtotais de cada linha de item. Quem tem a lista de itens? A classe
Venda. Portanto,Vendaé o Expert para calcular o total geral. - Para calcular o subtotal de uma linha de item (
quantidade * preco), precisamos do preço e da quantidade. Quem detém o preço unitário e a quantidade comprada? A classeItemVenda. Portanto,ItemVendaé o Expert para o subtotal do item.
- Para calcular o total de uma venda, precisamos dos subtotais de cada linha de item. Quem tem a lista de itens? A classe
sequenceDiagram
autonumber
actor Cliente as Usuário/UI
participant V as Venda (Expert do Total)
participant IV as ItemVenda (Expert do Subtotal)
Cliente->>V: calcularTotalGeral()
loop Para cada item na venda
V->>IV: calcularSubtotal()
IV-->>V: subtotal (preco * qtd)
end
V-->>Cliente: totalConsolidado
2.2 Prevenção do Anti-Padrão Feature Envy (Inveja de Recursos)
Quando uma classe A realiza sucessivas chamadas de getters em uma classe B para manipular os dados internos de B e produzir um resultado, dizemos que A tem “inveja de recursos” (Feature Envy) de B. O Information Expert cura esse sintoma movendo o algoritmo diretamente para dentro de B.
2.3 Quando Não Seguir o Expert?
Se a classe ItemVenda possui os dados de um item, deveríamos atribuir a ela a responsabilidade de executar a query SQL INSERT INTO itens_venda no banco de dados? Não! Fazer isso violaria frontalmente o Low Coupling e a High Cohesion, além de amarrar o modelo de domínio aos drivers do PostgreSQL. Nesses casos, o princípio cede espaço a outros padrões (como Pure Fabrication).
🏗️ 3. Creator (Criador)
“Assign class B the responsibility to create an instance of class A if B contains, aggregates, records, closely uses, or has the initializing data for A.” — Craig Larman
3.1 As 5 Cláusulas do Padrão Creator
Decidir qual entidade deve instanciar um novo objeto é um ponto crítico de acoplamento. A classe B deve ter a responsabilidade de instanciar objetos da classe A se uma ou mais das seguintes condições forem verdadeiras:
- Contenção / Composição:
Bcontém ou compõe instâncias deA(ciclo de vida amarrado). - Agregação:
Bagrega coleções de instâncias deA. - Registro:
Bregistra historicamente a ocorrência deA. - Uso Íntimo:
Butiliza instâncias deAde forma muito próxima e frequente. - Dados de Inicialização:
Bpossui as informações necessárias para passar ao construtor deAno momento da sua instanciação.
No caso do carrinho ou ponto de venda, a classe Venda agrega ItemVenda, contém ItemVenda e detém os parâmetros de quantidade e produto. Portanto, Venda deve conter o método adicionarItem(produto, qtd) e instanciar internamente ItemVenda, em vez de obrigar o controlador externo a criar o item manualmente.
🎮 4. Controller (Controlador)
“Assign the responsibility to receive or handle a system operation message to a non-UI class that represents the overall system, root object, or a use-case scenario.” — Craig Larman
4.1 A Fronteira entre Interface do Usuário e Lógica de Negócio
O Controller é o primeiro objeto além da camada de apresentação (interface visual gráfica, CLI, endpoint HTTP REST) que recebe os eventos do sistema e orquestra a execução da operação:
- Anti-Padrão UI Acoplada: Colocar regras de cálculo financeiro, validações e transações diretamente dentro dos manipuladores de eventos da tela (ex.:
btnFinalizarCompra_Click()). Qualquer tentativa de criar uma API web para o mesmo sistema exigirá reescrever toda a lógica. - Tipos de Controllers:
- Facade Controller: Representa o sistema como um todo, um subsistema ou a aplicação inteira (adequado para sistemas pequenos com poucas operações, ex.:
SistemaSupermercado). - Use-Case / Session Controller: Representa um caso de uso específico ou fluxo de trabalho (ex.:
ProcessarCheckoutController,AutenticacaoUsuarioController). É a abordagem recomendada em arquiteturas corporativas escaláveis.
- Facade Controller: Representa o sistema como um todo, um subsistema ou a aplicação inteira (adequado para sistemas pequenos com poucas operações, ex.:
4.2 O Anti-Padrão Bloated Controller (Controlador Inchado)
Um controlador torna-se “inchado” (bloated) quando o desenvolvedor o sobrecarrega com regras de negócio e cálculos que pertenciam aos Information Experts do domínio. O Controller deve agir como um maestro de orquestra, coordenando quem faz o trabalho, mas sem fazer o trabalho pesado por si próprio.
⚖️ 5. Low Coupling e High Cohesion: Os Padrões Avaliativos
Diferente de padrões operacionais, estes dois atuam como critérios permanentes de avaliação de qualquer decisão arquitetural:
5.1 Níveis e Tipos de Acoplamento
O acoplamento quantifica a dependência entre módulos:
- Acoplamento de Mensagens / Dados (Baixo / Desejável): Módulos comunicam-se passando parâmetros simples de tipos primitivos ou estruturas imutáveis.
- Acoplamento de Controle (Médio / Cuidado): Um módulo passa flags booleanas para ditar o fluxo de execução interno de outro módulo.
- Acoplamento de Conteúdo (Crítico / Inaceitável): Um módulo acessa ou modifica diretamente variáveis privadas de outro módulo.
5.2 Níveis e Tipos de Coesão
A coesão quantifica a focalização funcional de uma classe:
- Coesão Funcional (Ideal): Todos os métodos da classe trabalham harmoniosamente para realizar uma única tarefa bem definida.
- Coesão Sequencial (Boa): A saída de um método serve diretamente como entrada para o próximo método da classe.
- Coesão Coincidente / Utilitária (Baixa / Ruim): Agrupamento arbitrário de métodos utilitários sem conexão semântica mútua (
Utils,Helpers).
🛒 6. Estudo de Caso Passo a Passo: Ponto de Venda (PDV) com os 5 GRASP
Para consolidar as cinco heurísticas fundamentais, acompanhamos o processo cognitivo de design de um sistema de Ponto de Venda (PDV) durante a realização de uma venda de supermercado:
Passo 1: Quem recebe o clique de “Passar Produto no Caixa”?
- Dilema: O evento de leitura de código de barras ocorre na interface gráfica ou leitor óptico. Devemos colocar o cálculo ou a busca de banco no formulário da tela?
- Decisão GRASP (Controller): Não! Atribuímos a responsabilidade a uma classe não visual de controle:
ProcessarVendaController. A tela apenas captura o evento e delega a chamadacontroller.lerItem(codigoBarras, quantidade).
Passo 2: Quem cria a nova linha de compra (ItemVenda)?
- Dilema: O
ProcessarVendaControllerdeve darnew ItemVenda()diretamente? - Decisão GRASP (Creator): A classe
Vendaé a agregadora natural que contém a lista de linhas de item. Portanto, delegamos a responsabilidade de criação para a própriaVenda: o controller invocavenda.adicionarItem(produto, quantidade), e aVendainstanciaItemVenda, encapsulando a composição.
Passo 3: Quem calcula os valores parciais e totais?
- Dilema: Onde reside o cálculo financeiro?
- Decisão GRASP (Information Expert):
ItemVendaconhece sua própriaquantidadee o preço unitário do produto $\rightarrow$ calcula o subtotal do item.Vendaconhece a coleção de todas as instâncias deItemVenda$\rightarrow$ calcula o total bruto somando os subtotais.
Passo 4: Como evitar que a Venda fique amarrada ao PostgreSQL?
- Decisão GRASP (Low Coupling): Em vez de colocar chamadas de rede ou queries SQL dentro de
Venda, criamos uma interface abstrata de repositório (IRepositorioVendas), mantendo o modelo de domínio 100% puro e desacoplado.
Passo 5: Como garantir que o Controller não se torne um Blob?
- Decisão GRASP (High Cohesion): O
ProcessarVendaControllermantém coesão máxima porque sua única obrigação é coordenar a sequência do caso de uso (buscar produto no catálogo, solicitar adição à venda, notificar tela). Ele não realiza cálculos matemáticos nem gera comandos SQL.
🔄 7. Sinergia entre Padrões GRASP e Princípios SOLID
Existe uma correspondência direta entre as heurísticas de Craig Larman e os princípios de Robert C. Martin:
- Information Expert $\leftrightarrow$ SRP (Single Responsibility): Ao colocar o cálculo junto aos dados primários, a classe adquire uma única responsabilidade funcional focada.
- Low Coupling $\leftrightarrow$ DIP (Dependency Inversion): Reduzir acoplamento exige programar voltado a interfaces e abstrações estáveis.
- Controller $\leftrightarrow$ Arquitetura em Camadas: O Controller atua como a fronteira estrita entre a camada de apresentação e a camada de aplicação/domínio.
📋 8. Checklist de Auditoria GRASP em Code Reviews
Ao revisar pull requests ou refatorar sistemas legados, utilize as seguintes perguntas de auditoria para identificar infrações aos padrões GRASP fundamentais:
- A tela gráfica ou controller REST está fazendo cálculos ou SQL diretamente?
- Diagnóstico: Violação severa de Controller e High Cohesion.
- Ação Corretiva: Extraia a operação para um Controller de Caso de Uso e delegue a lógica ao domínio.
- O código consulta mais de 3 getters de uma entidade externa para calcular um resultado?
- Diagnóstico: Violação de Information Expert e manifestação de Feature Envy.
- Ação Corretiva: Mova o método diretamente para a classe que detém esses atributos.
- Quem está dando
newno objeto filho?- Diagnóstico: Violação de Creator quando classes externas instanciam dependências que pertencem ao ciclo de vida de uma entidade agregadora.
- Ação Corretiva: Forneça um método de fábrica na classe agregadora (ex.:
carrinho.adicionarItem(...)).
- Uma alteração no schema do PostgreSQL forçará a alteração de testes unitários do domínio?
- Diagnóstico: Violação de Low Coupling.
- Ação Corretiva: Isole o acesso a dados atrás de interfaces abstratas de repositório.
- A descrição do propósito da classe precisa de conjunções aditivas (“Esta classe faz X E faz Y E faz Z”)?
- Diagnóstico: Violação de High Cohesion.
- Ação Corretiva: Decomponha a classe em módulos menores, cada qual com um propósito singular.
📊 9. Matriz Comparativa dos Padrões GRASP Fundamentais
| Padrão GRASP | Natureza | Pergunta Chave de Design | Principal Benefício Arquitetural |
|---|---|---|---|
| Information Expert | Atribuição | Quem possui os dados necessários para realizar este cálculo ou validação? | Encapsulamento máximo e lógica próxima dos dados |
| Creator | Atribuição | Quem agrega, contém ou possui os dados para inicializar este novo objeto? | Menor acoplamento na instanciação de objetos |
| Controller | Atribuição | Quem recebe o comando da UI e orquestra a execução da operação de negócio? | Desacoplamento total entre apresentação e domínio |
| Low Coupling | Avaliativo | Esta decisão de design introduz dependências desnecessárias entre módulos? | Alta manutenibilidade, reusabilidade e independência |
| High Cohesion | Avaliativo | As responsabilidades desta classe estão concentradas em um único propósito? | Classes claras, fáceis de testar, manter e compreender |