Exercícios — Interfaces e o Contrato de Comportamento

Aula 6 — Programação Orientada a Objetos

Autor

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

Aula Soluções

Questões discursivas

  1. Explique o conceito de Interface como um “Contrato de Comportamento” e comente por que ela representa a materialização máxima do paradigma da Caixa Preta. O que diferencia a tipagem pura de uma interface da tipagem baseada em herança de classes?

  2. Analise a técnica do “Mutador Cego”. Como uma interface moderna consegue orquestrar a alteração do saldo de uma conta (aplicar juros), se ela é estritamente proibida de possuir atributos de instância? Explique usando a ideia de inversão de conhecimento (cérebro vs. músculos).

  3. Explique por que o sistema de tipos estáticos do Java (garantir que um parâmetro seja double) é insuficiente para proteger a integridade das regras de negócio. Como o lançamento de exceções atua como fiscalizador do contrato quando os limites da tipagem são atingidos?

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 — Interface como Contrato de Comportamento
  • □ Numa interface Voador usada tanto por uma classe Passaro quanto por uma classe Drone, o contrato exige apenas o método voar() — nada nele obriga as duas classes a compartilharem qualquer atributo interno.
  • □ Métodos de interface, em Java, terminam com corpo de implementação obrigatório.
  • □ Se uma classe implementasse apenas dois dos três métodos declarados por uma interface, deixando o terceiro sem corpo e sem a própria classe ser declarada abstract, o código ainda compilaria normalmente, desde que @Override estivesse presente nos dois métodos implementados.
  • □ No limite em que a anotação @Override é removida de um método que de fato sobrescreve um método da interface, o comportamento em tempo de execução (qual método é chamado) muda.
NotaTeste 2 — Interface como Tipo Puro
  • □ A interface é chamada de “Tipo Puro” porque não impõe hierarquia de herança de estrutura.
  • □ Duas classes de famílias completamente diferentes podem compartilhar o mesmo tipo, desde que implementem a mesma interface.
  • □ Numa aplicação que precisa que tanto uma classe VeiculoEletrico quanto uma classe Gerador (duas famílias completamente diferentes de objetos) sejam tratadas como Recarregavel, implementar uma interface comum resolveria isso sem exigir que ambas compartilhem uma superclasse.
  • □ Herança de classe e implementação de interface são exatamente o mesmo mecanismo em Java.
NotaTeste 3 — Interfaces Modernas: Default, Static, Private
  • □ Se, em vez de usar default, a nova funcionalidade processarComLog fosse adicionada como um método abstrato comum na interface Pagavel, todas as classes existentes que já implementam Pagavel (como Pix) continuariam compilando sem nenhuma alteração.
  • □ No limite em que uma interface só contém métodos static (nenhum método abstrato, nenhum default), ainda seria possível instanciar essa interface diretamente com new, já que ela não teria mais nenhum método pendente de implementação.
  • □ Métodos private em interfaces (Java 9+) servem para compartilhar lógica auxiliar entre métodos default.
  • □ A introdução de default methods obriga todas as classes legadas a serem recompiladas e alteradas manualmente.
NotaTeste 4 — O Paradoxo do Mutador Cego
  • □ Uma interface pode orquestrar transições de estado, mesmo sem possuir atributos de instância próprios.
  • □ Numa interface ValidadorDeSenha com um método default validar() que aplica regras de comprimento e complexidade chamando getSenhaAtual() (abstrato), a classe concreta que armazena a senha real desempenha o papel dos “músculos”, enquanto a interface continua sendo o “cérebro” da regra.
  • □ O método default aplicarJuros sabe exatamente onde e como o saldo está fisicamente armazenado.
  • □ No limite em que só existe uma única classe implementando Pagavel em todo o sistema, centralizar aplicarJuros como default na interface, em vez de deixá-lo como método comum daquela única classe, ainda traria o mesmo ganho de evitar duplicação de código.
NotaTeste 5 — Interface vs. Classe Abstrata
  • □ Se uma interface Java declarasse um atributo public static final int LIMITE = 10, isso contradiria a regra de que interfaces não podem ter estado.
  • □ Numa aplicação de jogos em que uma classe PersonagemVoador precisa herdar atributos físicos comuns de uma classe abstrata Personagem e, ao mesmo tempo, prometer comportamentos de Voador e de Nadador, ela pode declarar extends Personagem implements Voador, Nadador na mesma linha.
  • □ Interfaces definem uma identidade (“o que é”); classes abstratas definem um papel (“o que faz”).
  • □ Num sistema bancário em que ContaCorrente e ContaPoupanca compartilham o atributo protegido saldo e a lógica de validação de saque, mas cada uma tem regras de rendimento completamente diferentes, uma classe abstrata ContaBancaria (não uma interface) é a ferramenta mais adequada para compartilhar esse estado protegido.
NotaTeste 6 — Tipos como Comportamento
  • □ O tipo de um objeto orientado a objetos é definido pelas mensagens às quais ele responde, não pelo que ele guarda.
  • □ Numa hierarquia de coleções, ArrayList e HashMap não compartilham nenhuma superclasse de dados em comum além de Object, mas ambas poderiam implementar uma interface Limpavel com um método limpar(), tornando-as do mesmo tipo Limpavel para quem só precisa chamar esse método.
  • □ Declarar uma variável como Pagavel informa ao compilador exatamente qual classe concreta ela contém.
  • □ Se o CheckoutController declarasse a variável formaEscolhida como Pix em vez de Pagavel, mesmo que hoje só Pix fosse usado na prática, isso já eliminaria a vantagem de programar para a abstração caso um Cripto implements Pagavel surgisse amanhã.
NotaTeste 7 — Os Limites do Sistema de Tipos
  • □ No limite em que o parâmetro fosse declarado como um tipo customizado ValorMonetario (em vez de double primitivo) que só pudesse ser construído com valores positivos, a validação de negócio migraria do corpo do método para o próprio sistema de tipos, em tempo de compilação.
  • □ Um preço negativo passado para criarCobranca(double valor) é rejeitado automaticamente pelo compilador.
  • □ Num sistema de reservas de voos que aceita um parâmetro int quantidadeAssentos, um valor negativo passa a validação de tipo do compilador da mesma forma que um valor positivo — a mesma lacuna do sistema de tipos vista para o valor de pagamento desta aula se repete aqui.
  • □ Uma exceção é uma forma de o objeto dizer “recebi o tipo certo, mas os dados violam minhas regras”.
NotaTeste 8 — Fail-Fast e o Erro de Silenciar
  • □ Retornar null, 0 ou false para sinalizar erro é uma boa prática recomendada nesta aula.
  • □ Se, em vez de lançar a exceção imediatamente ao detectar valor <= 0, o método criarCobranca apenas registrasse o erro num log interno e continuasse a execução normalmente até o fim, isso ainda seria uma aplicação válida da filosofia Fail-Fast.
  • □ Falhas silenciosas tendem a se manifestar mais tarde, em pontos distantes da causa raiz do problema.
  • □ Lançar uma exceção no ponto exato da violação facilita o rastreamento via stack trace.
NotaTeste 9 — Try-Catch como Barreira de Contenção
  • □ O bloco try-catch no CheckoutController protege a experiência do usuário contra falhas de baixo nível.
  • □ Segundo a diretriz desta aula, componentes de baixo nível devem capturar e silenciar suas próprias exceções.
  • □ No limite em que não existe nenhum componente de alto nível entre o objeto que lança a exceção e o usuário final (a exceção se propaga direto até a interface), a aplicação da diretriz desta aula ainda garantiria uma mensagem de erro tratada e amigável para o usuário.
  • □ Um catch bem posicionado evita que uma falha externa (rede, banco) derrube o fluxo principal do sistema.
NotaTeste 10 — Novos Contratos: Notificação, Desconto, Logística
  • □ Numa aplicação de streaming de vídeo que hoje só grava logs de erro em arquivo local, trocar para uma interface Logavel implementada por um adaptador de nuvem seguiria a mesma lógica de Notificavel: o consumidor do log não precisa mudar uma linha.
  • □ Um double desconto isolado, sem uma interface EstrategiaDesconto, já protege contra valores absurdos como 500%.
  • □ Se, em vez de uma interface ServicoLogistico, o Pedido chamasse diretamente uma classe concreta TransportadoraCorreios para gerar o código de rastreio, trocar de transportadora no futuro não exigiria nenhuma alteração em Pedido.
  • □ No limite em que um método de interface declara throws Exception (a superclasse mais genérica possível) em vez de uma exceção específica como CarrierUnavailableException, a cláusula do contrato continua igualmente informativa sobre como e por que o método pode falhar.
NotaTeste 11 — Exceção como Cláusula de Contrato
  • □ Numa API de pagamento internacional que declara throws CurrencyConversionException, isso avisa ao consumidor da API que a conversão de moeda pode falhar por fatores fora do controle do próprio método (ex.: serviço de cotação indisponível), da mesma forma que criarCobranca avisa sobre falhas de validação.
  • □ Se um método lançasse uma exceção apenas para simular uma falha que nunca ocorre de fato na prática, isso ainda seria consistente com a ideia de exceção como “honestidade sobre um risco real”.
  • □ Num sistema de pagamento que, ao receber uma IllegalArgumentException de um valor inválido, registra o erro, notifica o usuário e continua processando os próximos pedidos da fila sem interromper o serviço inteiro, isso demonstra o mesmo ato de resiliência descrito nesta aula para o tratamento de exceções.
  • □ Uma vez capturada, uma exceção deve sempre ser ignorada silenciosamente para não incomodar o usuário.
NotaTeste 12 — Síntese: Contratos e Responsabilidade
  • □ No limite em que Pedido dependesse de dez interfaces diferentes em vez de três, o princípio de que Pedido “não sabe como”, só “confia no contrato”, deixaria de valer, porque o número de dependências já seria alto demais para manter esse desacoplamento.
  • □ Se EstrategiaDesconto não declarasse throws nem lançasse nenhuma exceção em caso de valor inválido, apenas retornando 0 (desconto nulo) silenciosamente, Pedido ainda estaria recebendo a mesma garantia de honestidade do contrato que as demais interfaces desta aula oferecem.
  • □ Programar contra interfaces e proteger regras com exceções são práticas independentes, sem relação entre si.
  • □ Esse design permite trocar implementações concretas em tempo de execução sem alterar o núcleo do sistema.