Decomposição e Responsabilidade

Aula 4 — Programação Orientada a Objetos

Marcos M. Raimundo — Instituto de Computação, UNICAMP

2026-08-26

Proposta da Aula

Até aqui: o objeto protegido (encapsulamento) e bem-comportado (Demeter).

O problema que sobra: um objeto pode ser tudo isso e ainda fazer coisas demais.

Roteiro: Classe Deus → SRP → o lado sombrio do SRP → tipos de associação → delegação.

A Classe Deus

Pedido: Cinco Responsabilidades, Uma Classe

public class Pedido {
    public double calcularTotal() { ... }        // 1. Negocio
    public double calcularICMS() { ... }          // 2. Impostos
    public String gerarReciboTexto() { ... }      // 3. Apresentacao
    public void salvarNoBanco() { ... }           // 4. Persistencia
    public void enviarConfirmacaoEmail() { ... }  // 5. Rede
}

Mudar imposto, banco ou layout do recibo — em todos os casos, mexe-se em Pedido.

4 problemas: fragilidade, acoplamento com infraestrutura, difícil reuso, carga cognitiva alta.

Coesão e Acoplamento

Coesão: O Foco Interno

Caixa só com chaves de fenda = alta coesão. Caixa com sanduíche + sapato + controle remoto = baixa coesão.

Acoplamento: O Vínculo Externo

Ligar a TV exigindo abrir a geladeira primeiro = alto acoplamento — dois sistemas amarrados sem necessidade.

O santo graal: alta coesão + baixo acoplamento.

SRP e o Teste do “E”

O Teste do “E”

“A classe Pedido calcula o total E processa o pagamento E notifica o cliente.” → responsabilidades demais.

Antes: Tudo Misturado

public void finalizarPedido() {
    double total = carrinho.getTotal();                              // calculo
    System.out.println("Processando R$ " + total + " no cartao...");  // pagamento
    System.out.println("Enviando e-mail...");                          // notificacao
}

Depois: DI + Delegação

public Pedido(Carrinho c, Pagavel p, ServicoEmail e) {
    this.carrinho = c; this.metodoPagamento = p; this.emailService = e;
}
public void finalizar() {
    metodoPagamento.processar(carrinho.getTotal());
    emailService.enviarConfirmacao(carrinho.getCliente());
}

Ganhos: testabilidade (mocks), reuso, manutenibilidade.

Por que testar Pedido.finalizar() sem Injeção de Dependência exigiria cobrar um cartão de crédito de verdade?

Escreva sua resposta e compare com um colega antes de avançar (2 min).

SRP, DI e Testabilidade

Dica

Julgue V ou F — a questão só conta se acertar os 4 itens:

  1. Uma classe que exige “E” para ser descrita numa frase provavelmente viola o SRP.
  2. Injeção de Dependência via construtor garante que o objeto nunca nasça sem suas colaborações essenciais.
  3. Sem DI, testar a lógica de Pedido exigiria disparar um e-mail real ou cobrar um cartão de verdade.
  4. Usar um “Mock” no lugar de Pagavel real é uma violação do princípio de Injeção de Dependência.

SRP, DI e Testabilidade — Resposta

Dica

  1. Verdadeiro — é o próprio Teste do “E”.
  2. Verdadeiro — é o ganho central da injeção via construtor.
  3. Verdadeiro — é exatamente o problema resolvido pela DI.
  4. Falso — usar um Mock é a aplicação correta de DI, não uma violação.

SRP Avançado: a Teoria do Ator e a Métrica LCOM

A Teoria do Ator

public double calcularPagamento() { ... }     // ator: CFO
public String gerarRelatorioHoras() { ... }   // ator: RH

Dois atores, uma classe → mudança de layout do RH força re-testar a folha de pagamento.

LCOM: a Prova Matemática

private String nome;       // so em salvarNome()
private String ipConexao;  // so em logAcesso()

Interseção de uso vazia = duas classes disfarçadas de uma (RepositorioUsuario + AuditoriaAcesso).

O Lado Sombrio do SRP

Cirurgia de Espingarda

Fragmentação excessiva: uma regra de negócio exige tocar em 15 classes minúsculas.

Modelo Anêmico (Revisitado)

public class Conta { public double saldo; }              // RUIM: sem comportamento
public class TransferenciaService {
    public void transferir(Conta o, Conta d, double v) { o.saldo -= v; d.saldo += v; }
}

Correto: Conta gerencia o próprio saldo (debitar()/creditar()) — SRP não é fragmentar tudo.

Tipos de Associação

Dependência (“Usa um”)

public boolean confereTotal(ArrayList<Produto> itens) { ... } // parametro, nao atributo

Vínculo mais fraco — como usar a caneta do balcão e devolver.

Agregação (“Tem um”)

private ArrayList<Produto> itens; // Carrinho "tem" Produtos
carrinho = null; // Carrinho morre; Produto sobrevive no catalogo!

Composição (“É parte de”)

public Cliente(String nome, double limite) {
    this.cartao = new Cartao(limite); // nasce e morre com o Cliente
}

Domínio decide: dados de cartão devem sumir com a conta → Composição, não Agregação.

Por que a escolha entre Agregação e Composição para Cliente/Cartao depende de uma decisão de negócio, e não só de como o código é escrito?

Escreva sua resposta e compare com um colega antes de avançar (2 min).

Tipos de Associação

Dica

Julgue V ou F — a questão só conta se acertar os 4 itens:

  1. Na Dependência, o objeto usado costuma ser recebido como parâmetro de método, não guardado como atributo.
  2. Na Agregação, destruir o “todo” implica destruir automaticamente a “parte”.
  3. Na Composição, a “parte” não tem sentido de existir fora do “todo” — nasce e morre com ele.
  4. A escolha entre Agregação e Composição pode depender de uma regra de negócio, não só da sintaxe do código.

Tipos de Associação — Resposta

Dica

  1. Verdadeiro — é a natureza passageira da Dependência.
  2. Falso — na Agregação, a parte sobrevive à destruição do todo; isso é característica da Composição.
  3. Verdadeiro — é a definição central de Composição.
  4. Verdadeiro — o exemplo do cartão de crédito privado do Cliente mostra exatamente isso.

Delegação: o Motor da Composição

Antes: o Naufrágio de Código

joao.getCartao().processar(500.0); // Principal "rouba" o cartao do Cliente

Depois: Delegação

public void pagar(double valor) {
    if (this.cartao == null) throw new IllegalStateException("Cliente sem cartao.");
    this.cartao.processar(valor); // delega ao especialista
}

joao.pagar(500.0); — o chamador nem sabe que Cartao existe.

Associação (ter) + Delegação (usar) = a composição fica tão poderosa quanto a herança, mais flexível.

joao.pagar(500.0) esconde a existência de Cartao de quem chama. Isso é o mesmo mecanismo da Lei de Demeter (Aula 3) ou algo diferente?

Escreva sua resposta e compare com um colega antes de avançar (2 min).

Delegação e Tell, Don’t Ask

Dica

Julgue V ou F — a questão só conta se acertar os 4 itens:

  1. joao.getCartao().processar(500.0) respeita a Lei de Demeter, pois Cartao é um atributo de Cliente.
  2. joao.pagar(500.0) é um exemplo de “Tell”: o chamador diz o que quer, sem saber como é feito.
  3. Depois da refatoração, o código que chama pagar() desconhece completamente a existência de Cartao.
  4. Associação (ter um objeto) e Delegação (usar esse objeto) juntas permitem trocar o Cartao sem afetar quem chama pagar().

Delegação e Tell, Don’t Ask — Resposta

Dica

  1. FalsoCartao ser atributo de Cliente não autoriza um objeto externo a navegar até ele; isso é o “naufrágio de código”.
  2. Verdadeiro — é a definição de “Tell” nesta aula.
  3. Verdadeiro — é o ganho central da delegação.
  4. Verdadeiro — é a síntese de Associação + Delegação apresentada nesta seção.

Conclusão

O que Aprendemos

  1. Classe Deus: sabe/faz demais — sintoma do “Teste do E”
  2. Coesão (foco interno) + baixo acoplamento (vínculo externo) = objetivo
  3. SRP tem limite: fragmentar demais gera Cirurgia de Espingarda e Anemia
  4. Dependência, Agregação, Composição + Delegação = a arquitetura viva

Próxima aula: interfaces como contrato formal, e testes automatizados com JUnit.