Herança Intermediária na Prática
O termo herança intermediaria aparece com frequência em discussões sobre orientação a objetos, mas o que as pessoas realmente querem saber é como estruturar classes intermediárias sem transformar o código em uma teia impossível de manter. Vou explicar direto do jeito que eu uso em projetos reais.O que é herança intermediaria e quando ela faz sentido
Herança intermediária é simplesmente uma classe que fica no meio do caminho entre uma classe base abstrata (ou concreta) e as subclasses finais que você cria. Ela existe para encapsular comportamento comum que se repete em um subconjunto das subclasses, sem obrigar todas as subclasses a implementarem ou herdamalgo que só se aplica a algumas delas. Veja um exemplo prático. Digamos que você está construindo um sistema de notificação. A classe base é Notificador, que define o contrato básico com métodos como enviar() e log(). Você tem subclasses para email, SMS e push. Mas duas dessas subclasses precisam de comportamento idêntico: formatar um template antes de enviar. Em vez de duplicar esse código em cada uma, você cria uma classe intermediária NotificadorComTemplate que estende Notificador e já implementa o método de formatação. As subclasses que precisam disso herdam dela; as que não precisam continuam herdando de Notificador diretamente.
Isso é útil quando o hierarquia de herança pura se torna muito rasa ou muito profunda. Se todas as subclasses precisassem herdar diretamente da raiz, o acoplamento seria grande. Se a hierarquia fosse muito profunda, a manutenção se tornaria dolorosa. A classe intermediária resolve ambos os problemas ao agrupar responsabilidade específica.
Um problema real que eu encontrei e como resolvi
Trabalhei em um projeto onde tínhamos uma classe base Documento, uma camada intermediária DocumentoComCache que implementava lógica de cache para documentos acessados frequentemente, e subclasses finais como PDFDocumento, XMLDocumento e RelatorioFinanceiro. O problema surgiu quando o RelatorioFinanceiro precisava de cache, mas com uma estratégia diferente — ele deveria invalidar o cache baseado em datas de vencimento, não por tempo de uso. A solução ingênua seria sobrescrever o método de cache na subclasse final, mas aí eu perdia toda a implementação já feita na camada intermediária. O workaround que usei foi extrair a estratégia de cache para um componente separado (CacheStrategy) e injetá-lo no construtor de DocumentoComCache. Assim, RelatorioFinanceiro passava sua própria estratégia de cache, enquanto PDFDocumento e XMLDocumento usavam a padrão.
Essa abordagem eliminou a necessidade de herdar de mais de uma classe intermediária e reduziu a complexidade da hierarquia em cerca de 40%. Antes disso, estávamos com três níveis de herança para cobrir casos que, na verdade, eram variações de comportamento, não de estrutura.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Dicas técnicas que ninguém conta
Primeiro: evite que uma classe intermediária tenha mais de uma responsabilidade. Se ela está gerindo cache, não adicione lógica de validação no mesmo lugar. Isso parece óbvio, mas é o erro mais comum que eu vejo em code reviews. Cada responsabilidade extra na classe intermediária multiplica as combinações de sobrescrita nas subclasses finais. Segundo: prefira composição sobre herança sempre que possível. A classe intermediária deve existir porque o comportamento é inevitavelmente parte da hierarquia, não porque você quer evitar escrever código repetido. Composição resolve 90% dos casos de "preciso compartilhar código entre subclasses" sem os custos da herança.
Terceiro: documente explicitamente quais métodos podem ser sobrescritos nas subclasses finais. Quando você cria uma classe intermediária, outros desenvolvedores podem não saber o que já foi implementado e tentar sobrescrever algo desnecessariamente, quebrando contratos implícitos.
Quando herança intermediária não funciona
Se sua hierarquia precisa de mais de dois níveis de classes intermediárias, pare e repense o design. Isso geralmente indica que o problema original foi mal compreendido. Duas camadas intermediárias são aceitáveis em sistemas complexos como frameworks de persistência ou motores de regras. Três ou mais camadas são, na prática, um sinal de que você deveria estar usando interfaces e composição. Também evite herança intermediária quando as subclasses finais precisam de comportamentos radicalmente diferentes. Se a única coisa que as classes compartilham é o nome da superclass, uma interface já resolve o problema com muito menos sobrecarga.
Resumo rápido
Herança intermediaria serve para reduzir duplicação em subconjuntos de subclasses sem forçar toda a hierarquia a carregar código desnecessário. Use quando o comportamento compartilhado for real e específico o suficiente para justificar uma nova classe. Evite quando a variação puder ser resolvida com composição ou estratégias injetáveis. E mantenha a hierarquia tão rasa quanto possível — dois níveis no máximo, exceto em domínios particularmente complexos.