Exercícios — Decomposição e Responsabilidade

Aula 4 — Programação Orientada a Objetos

Autor

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

Aula Soluções

Questões discursivas

  1. A classe Pedido do início desta aula tinha cinco responsabilidades (cálculo, imposto, formatação, persistência, e-mail). Escolha duas delas e explique, para cada uma, qual “ator” (departamento ou stakeholder) provavelmente solicitaria mudanças nela — e por que isso já é evidência de violação do SRP.

  2. Um colega decide aplicar o SRP “ao pé da letra” e separa a classe Pedido em 12 classes de uma linha cada, uma para cada campo. Explique, usando os conceitos de Cirurgia de Espingarda e Modelo Anêmico, por que essa fragmentação pode ser pior do que a Classe Deus original.

  3. Explique a diferença entre Agregação e Composição usando o exemplo de Cliente e Cartao. Por que essa escolha de design depende de uma decisão sobre o domínio de negócio (o que acontece com o cartão quando a conta é excluída), e não apenas de como o atributo é declarado em código?

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 — A Classe Deus (God Class)
  • □ É identificada por centralizar lógicas de múltiplos atores (financeiro, infraestrutura, UI) num único arquivo.
  • □ O “Teste do E” ajuda a diagnosticá-la: se a classe faz X E Y, provavelmente tem responsabilidades demais.
  • □ Uma Classe Deus facilita a manutenção, pois toda a lógica fica concentrada num só lugar.
  • □ Testar isoladamente uma responsabilidade de uma Classe Deus costuma exigir efeitos colaterais indesejados (e-mail, banco).
NotaTeste 2 — Coesão e Acoplamento
  • □ Se duas classes com métodos completamente distintos (sem nenhum atributo em comum) fossem fundidas numa única classe apenas para reduzir o número de arquivos do projeto, essa fusão aumentaria a coesão do sistema.
  • □ No limite em que uma classe A depende de tantos detalhes internos de uma classe B que qualquer mudança em B obriga a reescrever A, dizemos que A e B têm acoplamento muito baixo.
  • □ Numa oficina mecânica organizada em bancadas por especialidade (uma para motor, uma para elétrica, uma para funilaria), cada bancada guardando só as ferramentas da sua especialidade é uma analogia de alta coesão, assim como a caixa de ferramentas só com chaves de fenda desta aula.
  • □ Duas classes podem ter baixo acoplamento entre si mesmo que cada uma, isoladamente, tenha baixíssima coesão.
NotaTeste 3 — SRP e o Teste do “E”
  • □ Se uma classe tiver apenas um único método público, isso já garante que ela respeita o SRP.
  • □ Se a descrição de uma classe exige a conjunção “E”, ela provavelmente viola o SRP.
  • □ Aplicar o SRP significa que uma classe só pode ter um único método público.
  • Pedido que calcula total, processa pagamento e envia e-mail no mesmo método viola o SRP.
NotaTeste 4 — Injeção de Dependência
  • □ Se Pedido continuasse criando new CartaoDeCredito() e new ServicoEmail() dentro do próprio construtor, mas guardasse essas instâncias em atributos privados corretamente encapsulados, isso já seria suficiente para obter os ganhos de testabilidade da Injeção de Dependência.
  • □ A injeção via construtor garante que o objeto nunca exista sem suas dependências essenciais.
  • □ A DI aumenta o acoplamento, pois obriga o objeto a conhecer quem o instanciou.
  • □ Sem DI, testar Pedido.finalizar() isoladamente, sem efeitos colaterais reais, é muito mais difícil.
NotaTeste 5 — A Teoria do Ator no SRP
  • □ Numa fintech em que o mesmo microsserviço calcula o score de crédito (usado pelo time de Risco) e também formata esse score para exibição no app do cliente (usado pelo time de Produto), esse serviço, pela Teoria do Ator, tem apenas uma razão para mudar, pois lida com um único conceito: “score de crédito”.
  • □ Misturar lógica solicitada pelo RH e pelo Financeiro na mesma classe é seguro, pois são setores diferentes.
  • □ O conflito de atores é uma fonte comum de “quebras colaterais” em classes com múltiplas razões de mudança.
  • □ No limite em que uma empresa tem um único departamento responsável por todas as decisões de negócio (sem separação entre financeiro, RH, produto etc.), a Teoria do Ator deixaria de fazer qualquer distinção útil entre classes, pois haveria apenas um ator possível para todo o sistema.
NotaTeste 6 — A Métrica LCOM
  • □ No limite em que todo método de uma classe usa exatamente os mesmos atributos que todos os outros métodos, o LCOM dessa classe seria o mais alto possível.
  • □ Um LCOM alto indica uma classe muito coesa e bem projetada.
  • □ Se metade dos métodos usa o atributo A e a outra metade usa só o atributo B, a classe deveria ser dividida.
  • □ LCOM é uma forma de provar matematicamente que uma classe é, na prática, duas classes disfarçadas.
NotaTeste 7 — O Lado Sombrio do SRP
  • □ Num sistema de e-commerce em que adicionar um novo campo obrigatório (“CPF do comprador”) ao formulário de checkout exige editar 20 classes de uma linha cada (uma para nome, uma para e-mail, uma para CPF etc.), esse cenário ilustra a Cirurgia de Espingarda, e a solução recomendada pela aula é sempre criar ainda mais classes minúsculas para isolar o novo campo.
  • □ O Modelo Anêmico é uma consequência possível da aplicação fanática e sem pragmatismo do SRP.
  • □ O SRP proíbe que uma classe como Conta gerencie seu próprio saldo internamente.
  • □ Como o SRP recomenda alta coesão, uma classe Conta que gerencia tanto debitar() quanto creditar() sobre o mesmo saldo já é, por si só, uma violação leve do SRP, pois tem dois métodos que alteram o mesmo estado.
NotaTeste 8 — Associação: Dependência (“Usa um”)
  • □ Se um método recebesse um objeto como parâmetro e o armazenasse permanentemente num atributo da classe logo na primeira linha do método, essa relação continuaria sendo classificada como Dependência.
  • □ O objeto usado numa dependência costuma ser guardado como atributo de longo prazo.
  • □ Um método de uma API de pagamentos que recebe um objeto ContextoDeAuditoria só para registrar um log da chamada atual, sem guardar essa referência para uso posterior, exemplifica o mesmo tipo de vínculo fraco (Dependência) visto em Carrinho.confereTotal().
  • Carrinho.confereTotal(ArrayList<Produto> itens) recebendo a lista como parâmetro é um exemplo de dependência.
NotaTeste 9 — Associação: Agregação (“Tem um”)
  • □ Se, no exemplo do Carrinho e dos Produto, destruir o carrinho (carrinho = null) também apagasse os Produto do catálogo do sistema, essa relação ainda seria corretamente chamada de Agregação.
  • □ Se um Carrinho for destruído, os Produto que ele referenciava também são destruídos.
  • □ Vários objetos “todo” diferentes podem referenciar o mesmo objeto “parte” simultaneamente.
  • □ É uma forma de posse mais fraca do que a Composição.
NotaTeste 10 — Associação: Composição (“É parte de”)
  • □ No limite em que um objeto “parte” de uma Composição pudesse ser compartilhado por dois objetos “todo” diferentes ao mesmo tempo, a relação continuaria sendo, por definição, uma Composição.
  • □ Num sistema de RH em que cada Funcionario tem seu próprio HistoricoDisciplinar, criado apenas quando o funcionário é contratado e apagado quando o funcionário é desligado do sistema, essa relação seria mais bem classificada como Composição do que como Agregação.
  • □ Um Documento e suas Pagina são um exemplo clássico de Composição.
  • □ A escolha entre Agregação e Composição é sempre puramente sintática, nunca uma decisão de negócio.
NotaTeste 11 — Delegação
  • □ Se um método pagar() delegasse ao Cartao a operação de processar o valor, mas antes disso navegasse por this.cartao.getBanco().getGerente() só para registrar um log, essa navegação extra ainda seria uma delegação limpa, sem qualquer violação de Demeter.
  • joao.getCartao().processar(500.0) é um exemplo correto de delegação.
  • joao.pagar(500.0), delegando internamente ao Cartao, corrige a violação de Demeter do exemplo anterior.
  • □ A delegação é o mecanismo que torna a composição uma alternativa flexível à herança.
NotaTeste 12 — Tell, Don’t Ask e a Síntese da Aula
  • □ Num sistema de estoque em que, antes de vender um produto, o código lê produto.getQuantidade() e produto.getQuantidadeMinima() para decidir por fora se a venda é permitida, esse padrão está alinhado com “Tell, Don’t Ask”, da mesma forma que pedido.clientePodePagar() está.
  • □ Associação, SRP e Delegação são princípios independentes, sem nenhuma relação entre si.
  • □ Um sistema bem decomposto reduz a carga cognitiva ao permitir raciocinar sobre uma responsabilidade por vez.
  • □ Delegar ao especialista certo permite trocar sua implementação sem que o orquestrador precise mudar.