Exercícios — Acoplamento e Contratos

Aula 5 — Programação Orientada a Objetos

Autor

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

Aula Soluções

Questões discursivas

  1. Explique por que a separação de código em arquivos diferentes e o uso de construtores não são, por si só, garantia de desacoplamento. Diferencie acoplamento físico de acoplamento lógico, usando o exemplo de Pedido e Carrinho.

  2. Entre a classe Pedido e a classe Carrinho, qual deveria calcular o valor total da compra, segundo o princípio GRASP do Especialista na Informação? Justifique com base em quem possui os dados necessários.

  3. O que significa dizer que “módulos de alto nível não devem depender de módulos de baixo nível”? Explique como criar uma interface Pagavel inverte a dependência entre Cliente e Cartao.

Questões de Verdadeiro/Falso

Cada bloco de 4 itens trata do mesmo tema. A questão só é considerada correta se todos os 4 itens forem julgados corretamente (deixar em branco tem penalidade de 20% da nota da questão).

NotaTeste 1 — Falso Desacoplamento
  • □ O acoplamento físico (separação em arquivos) garante, por si só, o desacoplamento lógico entre classes.
  • □ Numa classe RelatorioFinanceiro que recebe um Carrinho pelo construtor e itera diretamente sobre seus itens internos para somar valores, a mesma falha de Feature Envy desta aula se aplica, mesmo em um contexto de relatório, não de pedido.
  • □ O paradigma da Caixa Preta permite acessar os dados internos de um colaborador associado, desde que via getter.
  • □ Ter a referência de um objeto não dá o direito de processar seus dados internos por fora dele.
NotaTeste 2 — Feature Envy (Inveja de Recursos)
  • □ No limite em que um método usa um único getter de um colaborador, uma única vez, para uma leitura simples e isolada, isso já caracteriza Feature Envy com a mesma severidade do exemplo de calcularCustoTotal() desta aula.
  • □ O sintoma clássico é o uso repetitivo de getters de um colaborador para processar dados externamente.
  • □ Se, em vez de mover a lógica de soma para o Carrinho (Move Method), apenas renomeássemos o método calcularCustoTotal() para somarItensExternamente(), o Feature Envy estaria resolvido.
  • □ Feature Envy não tem relação nenhuma com o princípio “Tell, Don’t Ask”.
NotaTeste 3 — A Métrica CBO
  • □ No limite em que uma classe depende de uma única classe externa, mas invoca dezenas de métodos diferentes dela, o CBO dessa classe continua sendo 1, independentemente do número de chamadas.
  • □ Refatorar de Feature Envy para delegação pura tende a reduzir o CBO da classe orquestradora.
  • □ Manter o CBO baixo reduz o risco de Efeito Cascata e facilita testes isolados.
  • □ O objetivo ideal de qualquer design é atingir CBO igual a zero.
NotaTeste 4 — Acoplamento de Conteúdo e Comum
  • □ Se o atributo itens do Carrinho fosse declarado private em vez de public, o método aplicarDescontoManual de Pedido ainda conseguiria fazer c.itens.clear() diretamente, sem nenhuma mudança adicional de design.
  • □ Atributos public favorecem diretamente o Acoplamento de Conteúdo.
  • □ O Acoplamento Comum via métodos estáticos facilita a substituição por mocks em testes automatizados.
  • □ Uma chamada estática oculta no corpo de um método é uma dependência mais difícil de enxergar do que uma no construtor.
NotaTeste 5 — Acoplamento de Estampa e de Dados
  • □ Num sistema de RH que passa um objeto Funcionario inteiro para um método que só precisa calcular o desconto do INSS a partir do salário, isso é o mesmo padrão de Acoplamento de Estampa visto no exemplo de NotificacaoEmail/Cliente desta aula.
  • □ Renomear a classe passada por Estampa pode quebrar o receptor, mesmo sem mudança na lógica de negócio.
  • □ No limite em que um serviço Notificacao recebe cada vez mais parâmetros primitivos individuais (não um objeto), manter o Acoplamento de Dados se torna, na prática, cada vez mais difícil de gerenciar, mesmo sem nenhuma perda de reutilização.
  • □ Refatorar de Estampa para Dados torna o serviço menos reutilizável, pois perde contexto.
NotaTeste 6 — GRASP: Especialista na Informação
  • □ Se duas classes, Pedido e Cliente, tivessem acesso igual aos dados de endereço de entrega, mas Cliente fosse quem originalmente recebe esse dado do usuário, o GRASP recomendaria colocar a lógica de validação de endereço em Pedido, e não em Cliente.
  • □ Segundo o GRASP, o Pedido deveria calcular o total, já que ele “contém” o Carrinho.
  • □ Num sistema onde o cálculo de imposto de um Produto está espalhado em três classes diferentes que só “pegam” o preço via getter, aplicar o Especialista na Informação implicaria mover essa lógica para dentro da própria classe Produto.
  • □ No limite em que uma única classe do sistema concentra toda a informação de todos os domínios de negócio, o GRASP do Especialista na Informação recomendaria centralizar ainda mais responsabilidades nela, já que ela já detém os dados.
NotaTeste 7 — O Limite da Composição
  • Cliente com private Cartao cartao acoplado a uma classe concreta é um exemplo do limite da composição básica.
  • □ Adicionar Pix e Boleto como novos atributos opcionais em Cliente, com if/else, respeita o OCP.
  • □ A explosão de complexidade ciclomática por if/else é um sintoma de acoplamento a classes concretas.
  • □ O acoplamento a implementações concretas impede a extensão Plug-and-Play do sistema.
NotaTeste 8 — Inversão de Dependência (DIP)
  • □ Se, em vez de uma interface Pagavel, Cliente dependesse de uma classe abstrata FormaDePagamentoAbstrata com métodos concretos parciais, isso ainda seria uma aplicação válida do DIP, desde que Cliente não referenciasse Cartao ou Pix diretamente.
  • □ Abstrações devem depender dos detalhes técnicos das implementações concretas.
  • □ O DIP permite erguer um “Muro de Fronteira” entre o núcleo do negócio e a infraestrutura.
  • □ Se, em vez de depender de Pagavel, Cliente continuasse com um único atributo Cartao cartao, mas todo acesso a ele passasse por blocos try/catch genéricos, isso já seria suficiente para dizer que Cliente aplicou o DIP.
NotaTeste 9 — O Princípio Aberto/Fechado (OCP) e o DIP
  • □ No limite em que um sistema nunca precisa de nenhuma extensão futura (o conjunto de comportamentos é fixo para sempre), a distinção entre respeitar ou violar o Princípio Aberto/Fechado deixa de ter qualquer consequência prática.
  • □ A cadeia de if/else sobre tipos concretos de pagamento é compatível com o OCP.
  • □ Depender de uma interface como Pagavel, em vez de classes concretas, favorece o cumprimento do OCP.
  • □ O DIP e o OCP trabalham juntos: abstrações estáveis permitem estender o sistema sem modificar o núcleo.
NotaTeste 10 — Barreira da Tipagem e o Gancho para Polimorfismo
  • □ Em Java, uma variável do tipo Pagavel pode referenciar Pix ou Cartao, desde que ambos implementem o contrato.
  • □ A composição pura, sem nenhum mecanismo de contrato, já é suficiente para essa substituição em tempo de execução.
  • □ Num sistema escrito numa linguagem de tipagem dinâmica, como Python sem type hints, a mesma capacidade de aceitar Pix ou Cartao através de uma única variável existiria por duck typing, sem nenhum mecanismo de contrato exigido em tempo de compilação.
  • □ No limite em que existisse só uma única forma de pagamento em todo o sistema (sem Pix, Boleto ou futuros métodos), a necessidade de Polimorfismo para resolver o problema de “múltiplas identidades” desapareceria por completo.
NotaTeste 11 — Cirurgia de Espingarda e Efeito Cascata
  • □ Se a lógica de soma do carrinho estivesse centralizada num único método de uma única classe (Move Method já aplicado), uma mudança na regra de desconto ainda exigiria caçar e repetir a alteração em quatro classes diferentes.
  • □ No limite em que um sistema tem uma única classe responsável por toda a lógica de negócio, sem nenhuma duplicação de regra em outro lugar, o risco de Cirurgia de Espingarda desaparece por definição, mesmo que o CBO dessa única classe seja extremamente alto.
  • □ Num sistema de e-commerce onde a lógica de cálculo de frete está duplicada em Pedido, NotaFiscal e RelatorioVendas, uma mudança na tabela de frete exigiria caçar e repetir a alteração nos três lugares — o mesmo Efeito Cascata/Cirurgia de Espingarda visto no exemplo de soma do carrinho.
  • □ Reduzir o CBO de uma classe para 1 (uma única dependência externa) já garante, por si só, que a lógica de negócio que ela usa não está duplicada em outras classes do sistema, prevenindo a Cirurgia de Espingarda.
NotaTeste 12 — Síntese: do Acoplamento ao Contrato
  • □ Coesão, CBO, Escala de Myers e GRASP são ferramentas complementares para diagnosticar e melhorar o design.
  • □ Resolver Feature Envy automaticamente resolve também o acoplamento a classes concretas do Bloco de DIP.
  • □ Um sistema bem desenhado combina baixo CBO, acoplamento de dados, especialistas corretos e abstrações estáveis.
  • □ A jornada desta aula termina exatamente onde a próxima começa: como impor contratos de verdade no compilador.