A pedido do exizt, estou expandindo meu comentário para uma resposta mais longa.
O maior erro que as pessoas cometem é acreditar que as interfaces são simplesmente classes abstratas vazias. Uma interface é uma maneira de um programador dizer: "Eu não me importo com o que você me dá, desde que siga esta convenção".
A biblioteca .NET serve como um exemplo maravilhoso. Por exemplo, quando você escreve uma função que aceita um IEnumerable<T>
, o que você está dizendo é: "Não ligo para como você está armazenando seus dados. Só quero saber que posso usar um foreach
loop.
Isso leva a um código muito flexível. De repente, para ser integrado, tudo o que você precisa fazer é seguir as regras das interfaces existentes. Se a implementação da interface for difícil ou confusa, talvez seja uma dica de que você está tentando enfiar uma estaca quadrada em um buraco redondo.
Mas surge a pergunta: "E a reutilização de código? Meus professores de CS me disseram que a herança era a solução para todos os problemas de reutilização de código e que a herança permite que você escreva uma vez e use em todos os lugares, além de curar a menangite no processo de resgate de órfãos. dos mares subindo e não haveria mais lágrimas e assim por diante etc. etc. etc. "
Usar herança apenas porque você gosta do som das palavras "reutilização de código" é uma péssima idéia. O guia de estilo de código do Google mostra esse ponto de maneira bastante concisa:
A composição é geralmente mais apropriada que a herança. ... [B] como o código que implementa uma subclasse se espalha entre a base e a subclasse, pode ser mais difícil entender uma implementação. A subclasse não pode substituir funções que não são virtuais, portanto, a subclasse não pode alterar a implementação.
Para ilustrar por que a herança nem sempre é a resposta, vou usar uma classe chamada MySpecialFileWriter †. Uma pessoa que acredita cegamente que a herança é a solução para todos os problemas argumentaria que você deveria tentar herdar FileStream
, para que não duplique FileStream
o código. Pessoas inteligentes reconhecem que isso é estúpido. Você deve apenas ter um FileStream
objeto em sua classe (como uma variável local ou membro) e usar sua funcionalidade.
O FileStream
exemplo pode parecer artificial, mas não é. Se você tiver duas classes que implementam a mesma interface exatamente da mesma maneira, deverá ter uma terceira classe que encapsule qualquer operação duplicada. Seu objetivo deve ser escrever classes que são blocos reutilizáveis independentes que podem ser montados como legos.
Isso não significa que a herança deve ser evitada a todo custo. Há muitos pontos a serem considerados e a maioria será abordada pesquisando a questão "Composição versus herança". Nosso próprio Stack Overflow tem algumas boas respostas sobre o assunto.
No final do dia, os sentimentos de seu colega de trabalho não têm a profundidade ou o entendimento necessários para tomar uma decisão informada. Pesquise o assunto e descubra por si mesmo.
† Ao ilustrar herança, todos usam animais. Isso é inútil. Em 11 anos de desenvolvimento, nunca escrevi uma classe chamada Cat
, por isso não vou usá-la como exemplo.
alligator
A implementação deeat
difere, é claro, na medida em que aceitacat
edog
como parâmetros.