Soluções — Padrões de Projeto: Vocabulário, Imutabilidade e a Gênese Segura do Objeto
Aula 11 — Programação Orientada a Objetos
Dica(Resposta) Teste 1 — O Vocabulário e a Natureza dos Padrões de Projeto
- ✗ Falso — Um Padrão de Projeto é um gabarito conceitual — a topologia de classes, responsabilidades e mensagens —, não um trecho de código para copiar e colar. Reaproveitar código-fonte literal de outro projeto, sem adaptar as classes ao novo domínio, é exatamente o antipadrão de confundir “padrão” com “biblioteca pronta”.
- ✔ Verdadeiro — É exatamente o argumento da “linguagem ubíqua”: entre dois engenheiros que compartilham o vocabulário, uma única palavra (“Observer”, “Strategy”) condensa toda uma topologia de classes, responsabilidades e protocolo de comunicação que, sem o nome, exigiria um parágrafo inteiro de explicação — e ainda assim correria risco de ambiguidade.
- ✗ Falso — Padrões de Projeto pressupõem que encapsulamento e troca de mensagens já estejam estabelecidos — eles não curam código procedural. Aplicar a estrutura de classes de um padrão sobre um sistema de variáveis globais e funções soltas apenas mascara o problema de fundo (a ausência de encapsulamento), sem resolvê-lo, ao contrário do que aconteceria num sistema já orientado a objetos.
- ✔ Verdadeiro — Padrões de Projeto resolvem problemas de acoplamento e coesão entre classes/objetos, não problemas algorítmicos de ordenação, criptografia ou busca — mesmo que ambos sejam soluções recorrentes e reutilizáveis, a natureza do problema resolvido é categoricamente diferente.
Dica(Resposta) Teste 2 — As Três Famílias de Padrões
- ✔ Verdadeiro — O padrão descrito (Decorator, citado em aula como exemplo de Estrutural) tem como foco a topologia — como compor objetos para formar uma estrutura maior e flexível —, não a gênese do objeto (Criação, que trata de como ele nasce) nem a comunicação no tempo (Comportamento).
- ✗ Falso — As três famílias descrevem o propósito arquitetural predominante de cada padrão isolado, não uma exclusividade mútua entre padrões de famílias diferentes num mesmo sistema. Strategy (Comportamental) e Factory Method (Criação) resolvem problemas ortogonais — um trata de comportamento intercambiável, o outro de gênese segura — e sua combinação no mesmo sistema é comum e desejável, não uma mistura indevida.
- ✔ Verdadeiro — A definição de família de Criação é exatamente essa — isolar a gênese e o ciclo de vida do
new. O fato de quase todos os 23 padrões GoF (de qualquer família) se apoiarem em composição e polimorfismo como mecânica interna não muda a classificação por propósito: o critério de família é “para que serve”, não “com que mecanismo é construído”. - ✗ Falso — É o oposto: a classificação por família depende do propósito (comunicação e distribuição de obrigações no tempo, no caso do Strategy), não do mecanismo estrutural usado (uma interface com múltiplas implementações). Esse mesmo mecanismo — interface com várias implementações — também aparece em padrões Estruturais e de Criação; o que decide a família é para que a variação existe, não como ela é implementada em Java.
Dica(Resposta) Teste 3 — Aliasing e a Fragilidade do Estado Mutável
- ✗ Falso — Em Java, atribuir uma variável de tipo referência a outra copia o endereço de memória, nunca os dados.
r2passa a apontar para o mesmo objetoRetangulona Heap quer1já apontava — não existe uma segunda cópia independente delargura/altura. - ✔ Verdadeiro — O mecanismo do aliasing não depende do domínio (retângulos, configurações, ou qualquer outra classe) — depende apenas de duas condições: mutabilidade e uma referência compartilhada. Qualquer classe mutável compartilhada entre módulos está exposta ao mesmo risco estrutural.
- ✗ Falso — A Cópia Defensiva elimina o risco de aliasing naquele ponto específico, mas tem custo real: sobrecarrega o Garbage Collector com objetos descartáveis extras e polui o código com lógica de clonagem repetida em cada fronteira de compartilhamento — não é uma solução “de graça”.
- ✗ Falso — O risco de aliasing bug nasce especificamente da possibilidade de mutação por uma referência compartilhada. Se o objeto lido é imutável, nenhuma leitura pode alterá-lo “pelas costas” de quem mais o referencia — o cenário de corrupção silenciosa simplesmente não existe para um parâmetro imutável, ao contrário de um mutável.
Dica(Resposta) Teste 4 — Entidade vs. Value Object
- ✔ Verdadeiro — A necessidade de distinguir dois objetos com atributos de valor idênticos é exatamente o critério que exige identidade persistente (um número de conta, por exemplo) — a marca registrada de uma Entidade. Um Value Object, cuja igualdade é por conteúdo, tornaria as duas contas indistinguíveis.
- ✗ Falso — É exatamente o oposto do que define um Value Object:
equals()compara valores (quantia e moeda), não endereços de memória. DoisDinheirocom o mesmo conteúdo devem serequals()independentemente de onde ou quando foram instanciados — essa é a substituibilidade que motiva o padrão. - ✔ Verdadeiro — Não há necessidade de identidade persistente aqui — o que importa é o conteúdo do endereço (rua, número, cidade, CEP), não “qual instância específica” foi usada. Dois
Enderecos com os mesmos dados são intercambiáveis para todos os efeitos práticos, exatamente o critério de um Value Object. - ✔ Verdadeiro — Entidade e Value Object não são mutuamente exclusivos dentro do mesmo grafo de objetos — uma Entidade tipicamente contém Value Objects como parte do seu estado mutável (o RA de
Alunocontinua sendo a identidade persistente, mas o valor do campoenderecopode ser trocado por uma nova instância imutável deEnderecosem que isso mude a identidade doAluno).
Dica(Resposta) Teste 5 — Obsessão por Primitivos e Anemia Semântica
- ✔ Verdadeiro — O tipo
intem Java aceita qualquer valor dentro do seu intervalo numérico, sem nenhuma noção de domínio de negócio — o compilador não tem como saber que uma idade nunca deveria ser negativa. É exatamente o risco de erros que deveriam ser de compilação (ou ao menos de construção Fail-Fast) tornarem-se, na prática, erros de runtime ou nem serem detectados. - ✗ Falso — Essa divergência de validação é exatamente o sintoma da “lógica dispersa” que a Obsessão por Primitivos causa — longe de inofensiva, ela gera inconsistência real: um CPF aceito por uma parte do sistema pode ser rejeitado por outra, produzindo comportamento imprevisível. Um Value Object
CPFcentralizaria essa validação num único lugar (o construtor), eliminando a divergência por definição. - ✔ Verdadeiro — É exatamente o mecanismo Fail-Fast do Value Object aplicado a um domínio diferente do
Email/Dinheirovistos em aula: a validação migra do ponto de uso (potencialmente distante, potencialmente em produção) para o momento da construção — se o objetoTaxaDeJurosexiste, ele já é garantidamente válido em qualquer lugar do sistema. - ✗ Falso — É a definição invertida: a Anemia Semântica ocorre exatamente quando o tipo primitivo não descreve o que o dado significa para o negócio —
Stringdiz “isto é uma sequência de caracteres”, nunca “isto é um e-mail válido” ou “isto é um CPF”. A perda de informação semântica é o próprio problema, não sua ausência.
Dica(Resposta) Teste 6 — O Padrão Builder e a Interface Fluente
- ✔ Verdadeiro — Encadear chamadas de método em Java (
objeto.metodo1().metodo2()) só é possível porque cada chamada intermediária devolve uma referência sobre a qual o próximo método pode ser invocado — no Builder, essa referência é a própria instância do Builder (return this), o que faz do encadeamento fluente possível. - ✗ Falso — Se a validação estivesse dentro de
comDesconto(), ela só teria acesso ao valor detotalacumulado até aquele ponto da cadeia — secomItem()(que incrementatotal) for chamado depois, a validação já executada emcomDesconto()estaria comparando o desconto contra um total ainda incompleto. É exatamente por isso quebuild(), executado só ao final, é o único lugar correto para validações cross-field que dependem do estado final e completo do objeto. - ✗ Falso — Se o construtor de
Pedidofosse público, qualquer código externo poderia chamarnew Pedido(...)diretamente, contornando inteiramente oPedidoBuildere suas validações cross-field. A blindagem depende especificamente do construtor serprivate, forçando toda instanciação a passar pelo portão único dobuild(). - ✔ Verdadeiro — É exatamente o design discutido em aula: o
PedidoBuilderé deliberadamente mutável (seus campos são reatribuídos a cada método de configuração) porque sua função é acumular estado temporário; oPedidofinal é imutável porque sua função é representar um estado de negócio estável e protegido. As duas classes têm papéis diferentes e, por isso, regras de mutabilidade diferentes.
Dica(Resposta) Teste 7 — Factory Method e Desacoplamento
- ✗ Falso — Embora
NotificacaoPedidoEmailseja um subtipo deNotificacaoPedido(então a atribuição a uma variável do tipo interface ainda compilaria por polimorfismo), o problema real é de assinatura declarada: se o métodocriardeclarasse formalmente o retorno comoNotificacaoPedidoEmail, ocase "SMS"do mesmo método (que devolveNotificacaoPedidoSMS) deixaria de compilar, poisNotificacaoPedidoSMSnão é umNotificacaoPedidoEmail. Só declarar o retorno como a interface comum permite que a fábrica devolva qualquer uma das implementações. - ✗ Falso — É exatamente o benefício central do Factory Method: a nova classe mais um novo caso no
switch/ifda fábrica bastam. Nenhum dos pontos de chamada precisa mudar, porque todos eles já dependem apenas da interfaceNotificacaoPedido, nunca de uma classe concreta específica. - ✔ Verdadeiro — Mesma topologia — uma fábrica decide, com base num critério de negócio, qual classe concreta instanciar, devolvendo sempre a abstração comum (
Convite) — aplicada a um domínio diferente do e-commerce. - ✔ Verdadeiro — É exatamente o benefício de “encapsulamento de parâmetros de infraestrutura”: a fábrica pode gerenciar dependências específicas de cada implementação concreta (credenciais de SMTP, chaves de API de gateway) sem que o cliente que apenas pede uma
NotificacaoPedidoprecise conhecer nenhum desses detalhes.
Dica(Resposta) Teste 8 — Inversão de Dependência na Criação
- ✗ Falso — É exatamente o problema do acoplamento rígido: se
ServicoDePedidosinstanciaRepositorioPedidosMySQLdiretamente, não existe nenhum ponto de substituição para um mock sem editar o próprio código-fonte deServicoDePedidos— a dependência está fundida na implementação, não injetada de fora. - ✔ Verdadeiro — É a definição de DIP aplicada à criação: o núcleo de negócio (
ServicoDePedidos) passa a depender só do contratoRepositorioPedidos, e a decisão concreta (“qual banco”, “qual fornecedor”) é tomada no ponto de entrada da aplicação, que injeta a fábrica apropriada — exatamente o mesmo raciocínio de fronteira aplicado aPagavelna Aula 10. - ✗ Falso — O DIP é sobre a direção da dependência (módulos de alto nível dependendo de abstrações, não de implementações concretas), não sobre quantas implementações concretas existem no momento. Mesmo com uma única implementação hoje,
ServicoDePedidosjá está corretamente desacoplado — pronto para uma segunda implementação (ou um mock de teste) sem nenhuma mudança em sua própria lógica. - ✔ Verdadeiro — É exatamente o paralelo traçado ao final do bloco: assim como
Pedidoconhece sóPagavel(nuncaPix/Cartao/Boletodiretamente) para o comportamento de pagamento,ServicoDePedidosconhece sóRepositorioPedidos(nuncaRepositorioPedidosMySQLdiretamente) para a persistência — o mesmo princípio de fronteira, aplicado uma vez ao comportamento (Strategy) e outra vez à criação (Factory Method + DIP).