Soluções — O Colapso da Herança e a Era da Composição: o Padrão Strategy
Aula 10 — Programação Orientada a Objetos
Dica(Resposta) Teste 1 — Fusão Estrutural e o Custo da Herança de Estado
- ✔ Verdadeiro — A alocação de memória para um campo declarado na classe base é incondicional — a JVM reserva espaço para cada atributo herdado em toda instância da linhagem, independentemente de o código da subclasse jamais ler ou escrever naquele campo. “Nunca acessar” é uma decisão de código; “não alocar” exigiria que o campo simplesmente não existisse naquela hierarquia — é exatamente o fenômeno da Subclasse Gorda.
- ✗ Falso — É a “Ilusão da Produtividade” às avessas: eliminar duplicação de código não é o mesmo que reduzir acoplamento — na verdade, herdar sem relação de identidade real aumenta o acoplamento estrutural (a subclasse passa a depender de toda a superfície pública e das mudanças futuras do construtor/estrutura da base), trocando um problema barato de resolver (duplicação) por um caro (acoplamento estrutural rígido).
- ✔ Verdadeiro — É exatamente o argumento de “Encapsulamento Preservado” da Era da Composição: por composição,
PedidoInternacionalsó interage comPedidoatravés de seus métodos públicos, então qualquer refatoração interna dePedido(como o formato de armazenamento de itens) fica oculta, desde que a interface pública seja mantida. Por herança, o acesso a detalhes viaprotectedcria justamente o tipo de dependência que uma mudança “inocente” na base pode quebrar sem aviso. - ✔ Verdadeiro — É a mesma estrutura de erro transportada para outro domínio: herdar por conveniência de método (ganhar
calcularHorasTrabalhadas()de graça) sem que exista a necessidade real de tratarFuncionarioTerceirizadocomo umFuncionarioem todo lugar do sistema (por exemplo, na geração da folha de pagamento) é exatamente a “Ilusão da Produtividade” — reuso de código sem relação de identidade genuína.
Dica(Resposta) Teste 2 — O Peso Físico na Heap: a Subclasse Gorda
- ✔ Verdadeiro — É a mesma estrutura do exemplo
Ebook/ProdutoBasetransportada para outro domínio: um atributo semanticamente relevante só para uma parte da hierarquia (filmes de cinema), declarado na base, é alocado para toda instância descendente — inclusive as que nunca usarão aquele dado. - ✔ Verdadeiro — A Subclasse Gorda é especificamente sobre atributos de estado desperdiçados na Heap — se a base não declara nenhum campo, não há nada para alocar desnecessariamente por causa dela, independentemente da profundidade da hierarquia. O fenômeno pode ainda existir por causa de atributos declarados em outras classes intermediárias, mas não por causa dessa base específica sem estado.
- ✗ Falso — Confunde dois problemas distintos: visibilidade (quem pode acessar o campo) e alocação de memória (se o campo existe fisicamente na instância). Trocar
protectedporprivaterestringe o acesso direto pelas subclasses, mas não impede que a JVM aloque espaço para aquele campo em toda instância da linhagem — o campo continua existindo fisicamente, só fica inacessível por nome de fora da própria classe. - ✔ Verdadeiro — É a correção por composição sugerida na aula: ao mover os campos logísticos para uma classe separada, só as classes que realmente compõem uma instância de
DadosLogisticosFisicos(produtos físicos) pagam o custo de memória associado — umEbook, que nunca precisaria dessa composição, deixa de carregar esses bytes desperdiçados.
Dica(Resposta) Teste 3 — Explosão Combinatória e Eixos Ortogonais
- ✔ Verdadeiro — O crescimento é multiplicativo: \(2^3 = 8\) com três eixos, \(2^4 = 16\) com quatro — cada eixo binário adicional dobra o número de combinações possíveis, o mesmo padrão exponencial discutido na aula.
- ✗ Falso — A explosão combinatória é uma propriedade estrutural da tentativa de modelar múltiplos eixos independentes por herança — não depende da natureza do eixo (técnico ou de negócio). “Categorias Promocionais” é um exemplo de terceiro eixo que dispararia a mesma explosão; qualquer eixo verdadeiramente ortogonal aos já existentes produz o mesmo efeito multiplicativo.
- ✔ Verdadeiro — Dois eixos binários independentes (Tipo de Veículo × Perfil de Risco) modelados por herança pura geram exatamente \(2 \times 2 = 4\) subclasses-folha, com o mesmo risco de duplicação de regra e mesma inviabilidade de escalar para um terceiro eixo — a estrutura do problema é idêntica à do e-commerce, só o domínio muda.
- ✔ Verdadeiro — A explosão combinatória nasce especificamente da ortogonalidade dos eixos — cada combinação das opções de um eixo com as do outro sendo um cenário de negócio válido e necessário. Se uma regra de negócio elimina metade das combinações a priori (todo Importado já é Físico), o espaço de casos deixa de crescer multiplicativamente, e uma única subclasse extra pode bastar sem reabrir o problema.
Dica(Resposta) Teste 4 — A Proibição da Herança Múltipla em Java (Diamante)
- ✔ Verdadeiro — É a forma clássica do Problema do Diamante: se
ProdutoFisicoeProdutoImportadoherdassem ambos, por caminhos distintos, de uma mesma classeProduto, uma hipotéticaFisicoImportadoherdaria duas “cópias” conceituais do estado deProdutoatravés de cada caminho — ambiguidade que existe independentemente de haver ou não colisão de nomes de método entreProdutoFisicoeProdutoImportadodiretamente. - ✗ Falso — Interfaces com métodos
defaultpodem gerar conflito: se duas interfaces implementadas pela mesma classe declararem um métododefaultcom a mesma assinatura, o compilador exige que a classe resolva explicitamente a ambiguidade (sobrescrevendo o método). Múltiplas interfaces evitam o problema mais grave — ambiguidade de estado (campos duplicados) — mas não eliminam toda possibilidade de conflito de comportamento. - ✔ Verdadeiro — É exatamente a ambiguidade de estado que motiva a proibição: sem uma regra de desempate clara (qual
desconto, de qual superclasse, um acessothis.descontodentro deFisicoImportadodeveria retornar?), o comportamento do programa se tornaria imprevisível ou exigiria uma sintaxe de desambiguação explícita a cada acesso — complexidade que o Java optou por nunca introduzir para herança de classes. - ✔ Verdadeiro — Métodos
defaultpermitem que uma interface forneça uma implementação padrão de comportamento, algo que só herança de classes oferecia antes — mas interfaces nunca carregam estado (campos de instância), então a ambiguidade mais perigosa do Diamante (conflito de campos duplicados) simplesmente não tem como ocorrer nesse mecanismo, mesmo quando um conflito de métododefaultprecisa ser resolvido explicitamente.
Dica(Resposta) Teste 5 — Composição, Agregação e Associação
- ✔ Verdadeiro — O critério que distingue Agregação de Composição estrita é justamente a sobrevivência da parte à destruição do todo — se as partes continuam vivas e utilizáveis, a relação não tinha a posse exclusiva de vida característica da Composição estrita, encaixando-se na definição de Agregação (Todo-Parte sem dono absoluto da existência da parte).
- ✔ Verdadeiro — A definição de Associação usada na aula exige vidas completamente autônomas — nenhum dos dois lados depende da existência do outro. Amarrar a criação e a destruição do cartão ao ciclo de vida específico de um cliente introduz exatamente a dependência que a Associação, por definição, não tem; a relação descrita passaria a se parecer com Composição estrita (a “parte” só existe através do “todo” e morre com ele), não mais com a colaboração livre entre
ClienteeCartaoDeCreditovista na aula. - ✗ Falso — Guardar uma referência é a mecânica comum a Agregação e Associação — a diferença entre as duas não está em “ter uma referência”, mas no grau de autonomia dos ciclos de vida envolvidos. Classificar toda referência como Agregação ignora exatamente a distinção que a aula estabeleceu (Todo-Parte com posse parcial vs. colaboração totalmente livre).
- ✔ Verdadeiro — É a mesma estrutura do
CarrinhoDeCompras/Produtoaplicada a outro domínio: o “Todo” (Equipe) agrupa as partes (Jogador), mas a extinção do todo não implica a destruição das partes — elas seguem existindo e podem, inclusive, ser realocadas, exatamente o comportamento que caracteriza Agregação.
Dica(Resposta) Teste 6 — Quase-Decomponibilidade e Sistemas Modulares
- ✗ Falso — A “quase-decomponibilidade” de Simon exige ambos os lados da comparação: elos internos fortes (coesão) e dependências externas fracas — não apenas a ausência de dependência externa. Um sistema sem nenhuma coesão interna em suas peças não seria estável mesmo com zero acoplamento entre elas, porque cada peça, isoladamente, já seria frágil e desorganizada; e um sistema com dependência zero entre partes também deixaria de funcionar como um sistema coeso, incapaz de colaborar para um objetivo comum.
- ✔ Verdadeiro — A quase-decomponibilidade é sobre a força relativa dos elos internos vs. externos de fato, não sobre a aparência superficial da organização do código. Compartilhar variáveis globais e estruturas internas entre módulos cria dependências externas fortes exatamente onde deveriam ser fracas — a “modularidade” nesse caso é só uma fachada de nomenclatura, não uma propriedade estrutural real do sistema.
- ✔ Verdadeiro — Por herança, a lógica de logística estaria fundida na própria identidade de
Pedido— substituir a transportadora exigiria alterar a hierarquia de tipos dePedido(potencialmente afetando todo código que dependia daquele tipo). Por composição, basta trocar a implementação da interface de logística injetada, mantendoPedidoe seu tipo intocados — exatamente o argumento da analogia do eletrodoméstico modular. - ✗ Falso — Simplesmente dividir um sistema em muitas classes não garante resiliência se essas classes permanecerem fortemente acopladas entre si (por exemplo, todas dependendo de detalhes internas umas das outras). A quase-decomponibilidade — e a resiliência que dela decorre — depende da qualidade das interações (elos externos fracos, via contratos estáveis), não apenas da quantidade de divisões.
Dica(Resposta) Teste 7 — Identidade vs. Papel: a Falha da Herança no Checkout
- ✔ Verdadeiro — O aprisionamento estático é especificamente sobre a impossibilidade de trocar o meio de pagamento depois de instanciado o objeto — se essa troca nunca fosse necessária por regra de negócio, esse argumento específico perderia força (embora o problema de identidade-vs-papel e o risco de explosão da árvore, discutidos na aula, ainda permaneçam como razões independentes para preferir composição).
- ✗ Falso — “Centralizar em um único método” não é o mesmo que “coeso” — pelo contrário, o método passa a acumular responsabilidades de domínios completamente diferentes (antifraude de cartão, geração de QR Code Pix, código de barras de boleto), o oposto de coesão. É o code smell clássico discutido na aula:
Pedidopassa a conhecer detalhes de infraestrutura alheios ao seu domínio. - ✔ Verdadeiro — O plano de cobrança de um usuário, assim como o meio de pagamento de um pedido, é um papel temporário (pode mudar de mensal para anual), não uma característica que define permanentemente o que o
Usuarioé. Modelar essa variação por herança gera o mesmo aprisionamento estático: trocar de plano exigiria trocar a classe do objeto, o que Java não permite em tempo de execução. - ✔ Verdadeiro — É exatamente a solução que o padrão Strategy oferece: tratando o meio de pagamento como uma referência substituível (
Pagavel) injetada viaset, a troca de Pix para Cartão se resume a trocar o objeto apontado pela referência — oPedidopermanece o mesmo objeto na Heap durante toda a operação.
Dica(Resposta) Teste 8 — O Padrão Strategy e o Princípio Aberto/Fechado
- ✔ Verdadeiro — Mesmo com uma única implementação hoje, o Strategy já entrega encapsulamento (a lógica de
Pixfica isolada e testável sem instanciarPedido) e desacoplamento (Pedidonão conhece detalhes internos dePix) — os dois primeiros benefícios da composição discutidos na aula não dependem de já existir mais de uma estratégia em uso; a “intercambiabilidade” é um benefício adicional, não o único. - ✗ Falso — Instanciar a classe concreta diretamente dentro de
Pedidoreintroduz exatamente o acoplamento que a injeção de dependência elimina — qualquer novo meio de pagamento exigiria editarprocessarPagamento()para decidir quando instanciar a classe nova, violando o Princípio Aberto/Fechado que a interfacePagavele a injeção viaset/construtor foram desenhadas para garantir. - ✗ Falso — O OCP é especificamente sobre não precisar modificar
Pedidopara acomodar uma nova variação de comportamento intercambiável (um novo meio de pagamento) — não uma proibição geral de qualquer edição futura. Corrigir um bug genuíno na lógica de orquestração dePedido(por exemplo, uma condição mal escrita emfinalizarPedido()) é manutenção normal do código, categoricamente diferente de precisar reabrir o arquivo toda vez que a loja aceita um novo meio de pagamento. - ✔ Verdadeiro — É a mesma topologia formal do GoF aplicada a um eixo de variação diferente do mesmo domínio:
Pedido(Context) mantém uma referência aCalculadoraFrete(Strategy), e cada transportadora (Correios, Transportadora Privada, Retirada em Loja) é uma ConcreteStrategy intercambiável — a estrutura é idêntica à dePagavel/Pix/Cartao/Boleto, só o algoritmo variável muda (cálculo de frete em vez de processamento de pagamento).