Exercícios — Composição de Sistemas: Contratos e Estabilidade
Aula 3 — Programação Orientada a Objetos
Marcos M. Raimundo — Instituto de Computação, UNICAMP
Questões discursivas
Explique a metáfora “Fabricante de Peças vs. Arquiteto de Sistemas”. Por que a preocupação puramente anatômica (sintaxe,
this,private) é insuficiente para construir sistemas sustentáveis a longo prazo?O código
pedido.getCliente().getEndereco().getCEP()é um “Naufrágio de Código” (train wreck). Explique tecnicamente por que essa linha representa um risco arquitetural, e como um método de delegação resolveria o problema.Explique como a interface atua como um “escudo” para quem usa um objeto, usando a analogia do controle remoto ou do padrão USB-C para justificar por que não é preciso conhecer a “fiação interna” de um colaborador.
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).
- □ Se um projetista dedicasse todo o cuidado de design a otimizar cada classe isoladamente — sem nunca considerar como as mensagens fluem entre elas — o sistema resultante seria tão sustentável quanto um projetado com atenção à rede de colaboradores.
- □ Um sistema robusto pode ser visto como um amontoado de classes que expõem seus dados livremente.
- □ Projetar por “encaixes” (interfaces) permite ignorar o “como” interno de cada peça no rascunho do sistema.
- □ A maturidade de design é atingida ao definir as mensagens entre objetos antes de escrever a lógica interna dos métodos.
- □ Um objeto especialista deve, preferencialmente, não conhecer a existência de seu orquestrador.
- □
ItemCarrinho.subtotal()deve acessar diretamente o atributoprecoBasedeProduto. - □
Carrinho.calcularTotal()usa delegação, sem conhecer a lógica interna deItemCarrinho. - □ Dividir responsabilidades entre especialistas evita a criação de “Objetos Deus”.
- □ Se um método público de
ValidadorFinanceiropassar a ter bugs ocasionais depois de uma refatoração puramente interna, isso já é evidência de que a separação entre interface e implementação falhou naquele ponto. - □ Alterar a implementação de um método, mantendo sua assinatura, não deveria exigir recompilar o cliente.
- □ O acoplamento físico — conhecer as entranhas de outra classe — é uma das maiores causas de fragilidade.
- □ Uma interface bem desenhada deve expor o máximo possível da estrutura interna, para facilitar o reuso.
- □ Atributos privados garantem que a interface pública seja a única via de interação com o objeto.
- □ Ocultar a implementação permite que regras de negócio voláteis mudem sem efeito cascata.
- □ Proteger o estado interno é dispensável se o programador garantir que ninguém chamará os métodos errado.
- □
calcularPrecoDeVenda()pode mudar de fórmula internamente sem afetar quem já chama esse método.
- □ Se
getCEPDeEntrega()fosse reescrito para retornar o próprio objetoEnderecoem vez do CEP já extraído, e o chamador então fizessepedido.getCEPDeEntrega().getCep(), isso continuaria respeitando a Lei de Demeter tanto quanto a versão atual. - □ O objetivo é impedir que o conhecimento sobre a hierarquia interna do sistema se espalhe sem controle.
- □ “Falar com estranhos” (objetos obtidos navegando por outros objetos) aumenta a resiliência do código.
- □ Um método de delegação, como
getCEPDeEntrega(), é uma forma correta de respeitar a lei.
- □ É identificado por longas cadeias de chamadas, como
a.getB().getC().getD(). - □ O problema real é puramente estético — o excesso de pontos polui a leitura do código.
- □ Cada nível de navegação profunda é uma fronteira de objeto invadida.
- □ Se a estrutura intermediária (ex.:
Carteira) mudar, o código que a navegou diretamente quebra.
- □ Em vez de investigar o saldo do cliente para autorizar a venda, o sistema deve pedir ao
Pedidoque se valide. - □ “Perguntar” por dados para decidir por fora é uma prática que fortalece o encapsulamento.
- □ Mover a lógica para onde os dados residem evita que classes se tornem “fofoqueiras”.
- □
pedido.clientePodePagar()é um exemplo de “Tell”: oPedidodecide internamente como verificar isso.
- □ Num sistema de RH que calcula bônus a partir do
Departamentode umFuncionario, se o métodocalcularBonus()vive dentro deFuncionariomas lêthis.departamento.getMultiplicador()internamente, isso já configura uma violação de delegação, poisFuncionarioestá “chamando” um método de outro objeto. - □ Um “orquestrador” como
Carrinhodeve calcular pessoalmente o subtotal de cada item, para garantir precisão. - □ Feature Envy é o sintoma de uma classe mais interessada nos dados de outra do que nos próprios.
- □ A cura para Feature Envy é mover o método para a classe que de fato detém os dados necessários.
- □ Programar para um tipo genérico (
Pagavel) em vez de uma classe concreta (Boleto) aumenta a rigidez. - □ O uso de tipos genéricos permite que o sistema aceite novos componentes sem alterar o código já existente.
- □ Referenciar objetos por suas interfaces é uma recomendação central para promover flexibilidade.
- □ Um
Checkoutque recebePagavelfunciona com qualquer classe futura que implemente esse contrato.
- □ Um erro de compilação ao passar
Boletoonde se esperaCartaoDeCreditoé sempre um bug do compilador. - □ Esse erro pode revelar que o sistema exige uma identidade específica em vez de uma capacidade funcional.
- □ A solução típica é generalizar o parâmetro para uma interface que ambas as classes implementem.
- □ Elevar o nível de abstração do parâmetro reduz o acoplamento entre
Checkoute os métodos de pagamento.
- □ Um sistema pode ser “Razoável” segundo o princípio TRUE mesmo que o custo de adicionar uma funcionalidade simples cresça proporcionalmente ao tamanho total do código já escrito, contanto que o código seja bem documentado.
- □ Um código é “Razoável” quando o custo de uma mudança é proporcional ao benefício que ela traz.
- □ O design de caixa preta aumenta o custo de mudança, pois esconde erros atrás de interfaces.
- □ Contratos estáveis e baixo acoplamento evitam que o custo de uma nova funcionalidade cresça exponencialmente.
- □
Carrinhoatua como “maestro”: coordena especialistas em vez de fazer o trabalho de cada um. - □ A fronteira entre
CarrinhoeItemCarrinhoé respeitada quando oCarrinhosoma subtotais já calculados. - □ Se
ItemCarrinhomudar sua fórmula de subtotal,Carrinhoprecisa ser reescrito para acompanhar. - □ Um bom orquestrador permanece estável mesmo quando a lógica interna de um especialista evolui.