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:
    1. Responsabilidades de Saber (Knowing): Conhecer dados de encapsulamento privado, conhecer objetos relacionados ou conhecer coisas que podem ser derivadas ou calculadas.
    2. 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?
    1. 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.
    2. 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 classe ItemVenda. Portanto, ItemVenda é o Expert para o subtotal do item.
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:

  1. Contenção / Composição: B contém ou compõe instâncias de A (ciclo de vida amarrado).
  2. Agregação: B agrega coleções de instâncias de A.
  3. Registro: B registra historicamente a ocorrência de A.
  4. Uso Íntimo: B utiliza instâncias de A de forma muito próxima e frequente.
  5. Dados de Inicialização: B possui as informações necessárias para passar ao construtor de A no 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:
    1. 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).
    2. 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.

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:

  1. Acoplamento de Mensagens / Dados (Baixo / Desejável): Módulos comunicam-se passando parâmetros simples de tipos primitivos ou estruturas imutáveis.
  2. Acoplamento de Controle (Médio / Cuidado): Um módulo passa flags booleanas para ditar o fluxo de execução interno de outro módulo.
  3. 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:

  1. Coesão Funcional (Ideal): Todos os métodos da classe trabalham harmoniosamente para realizar uma única tarefa bem definida.
  2. Coesão Sequencial (Boa): A saída de um método serve diretamente como entrada para o próximo método da classe.
  3. 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 chamada controller.lerItem(codigoBarras, quantidade).

Passo 2: Quem cria a nova linha de compra (ItemVenda)?

  • Dilema: O ProcessarVendaController deve dar new 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ópria Venda: o controller invoca venda.adicionarItem(produto, quantidade), e a Venda instancia ItemVenda, 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):
    • ItemVenda conhece sua própria quantidade e o preço unitário do produto $\rightarrow$ calcula o subtotal do item.
    • Venda conhece a coleção de todas as instâncias de ItemVenda $\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 ProcessarVendaController manté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:

  1. 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.
  2. 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.
  3. Quem está dando new no 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(...)).
  4. 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.
  5. 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