Jame Gosling disse
"Você deve evitar a herança da implementação sempre que possível."
e, em vez disso, use herança de interface.
Mas por que? Como podemos evitar herdar a estrutura de um objeto usando a palavra-chave "extends" e, ao mesmo tempo, tornar nosso código Orientado a Objetos?
Alguém poderia dar um exemplo orientado a objetos que ilustra esse conceito em um cenário como "encomendar um livro em uma livraria?"
Respostas:
Gosling não disse para evitar o uso da palavra-chave extends ... ele apenas disse para não usar herança como um meio de conseguir reutilizar o código.
A herança não é ruim por si só e é uma ferramenta (essencial) muito poderosa para usar na criação de estruturas OO. No entanto, quando não é usado corretamente (ou seja, quando usado para outra coisa que não a criação de estruturas de Objeto), ele cria um código que é muito acoplado e muito difícil de manter.
Como a herança deve ser usada para criar estruturas polimórficas, isso pode ser feito com a mesma facilidade com interfaces, portanto, a instrução para usar interfaces em vez de classes ao projetar suas estruturas de objetos. Se você se encontra usando extends como forma de evitar o código de copiar e colar de um objeto para outro, talvez esteja usando errado e seria melhor atendido com uma classe especializada para lidar com essa funcionalidade e compartilhar esse código por meio da composição.
Leia sobre o princípio da substituição de Liskov e entenda. Sempre que você estiver prestes a estender uma classe (ou uma interface), pergunte a si mesmo se você está violando o princípio; se sim, é mais provável que (a) use herança de maneiras não naturais. É fácil de fazer e geralmente parece a coisa certa, mas tornará seu código mais difícil de manter, testar e evoluir.
--- Editar Esta resposta é um bom argumento para responder à pergunta em termos mais gerais.
fonte
Nos primeiros dias da programação orientada a objetos, a herança de implementação era o martelo de ouro da reutilização de código. Se havia alguma funcionalidade em alguma classe que você precisava, a coisa a fazer era herdar publicamente dessa classe (esses eram os dias em que o C ++ era mais prevalente que o Java) e você obtinha essa funcionalidade "de graça".
Exceto que não era realmente gratuito. O resultado foram hierarquias de herança extremamente complicadas que eram quase impossíveis de entender, incluindo árvores de herança em forma de diamante que eram difíceis de manusear. Isso é parte do motivo pelo qual os designers de Java optaram por não suportar herança múltipla de classes.
O conselho de Gosling (e outras declarações) como se fosse um remédio forte para uma doença bastante séria, e é possível que alguns bebês sejam jogados fora com a água do banho. Não há nada de errado em usar a herança para modelar um relacionamento entre classes que realmente é
is-a
. Mas se você quiser usar uma funcionalidade de outra classe, será melhor investigar outras opções primeiro.fonte
A herança reuniu bastante reputação assustadora recentemente. Um motivo para evitá-lo é o princípio do design de "composição sobre herança", que basicamente encoraja as pessoas a não usarem herança onde esse não é o verdadeiro relacionamento, apenas para economizar algum código. Esse tipo de herança pode causar alguns erros difíceis de encontrar e difíceis de corrigir. A razão pela qual seu amigo provavelmente estava sugerindo o uso de uma interface é porque as interfaces fornecem uma solução mais flexível e sustentável de APIs distribuídas. Com mais freqüência, eles permitem que alterações sejam feitas em uma API sem quebrar o código do usuário, onde a exposição de classes geralmente quebra o código do usuário. O melhor exemplo disso em .Net é a diferença de uso entre List e IList.
fonte
Porque você só pode estender uma classe, enquanto pode implementar muitas interfaces.
Uma vez ouvi dizer que a herança define o que você é, mas as interfaces definem um papel que você pode desempenhar.
As interfaces também permitem um melhor uso do polimorfismo.
Isso pode ser parte do motivo.
fonte
Também me ensinaram isso e prefiro interfaces sempre que possível (é claro que ainda uso herança onde faz sentido).
Uma coisa que eu acho que faz é separar seu código de implementações específicas. Digamos que eu tenha uma classe chamada ConsoleWriter e que seja passada para um método para escrever algo e que seja gravado no console. Agora, digamos que eu queira mudar para a impressão em uma janela da GUI. Bem, agora eu tenho que modificar o método ou escrever um novo que use o GUIWriter como parâmetro. Se eu iniciasse definindo uma interface IWriter e tivesse o método em um IWriter, poderia começar com o ConsoleWriter (que implementaria a interface IWriter) e depois escrever uma nova classe chamada GUIWriter (que também implementa a interface IWriter) e então eu teria apenas que mudar a classe que estava sendo aprovada.
Outra coisa (o que é verdade para C #, não tenho certeza sobre Java) é que você só pode estender 1 classe, mas implementar muitas interfaces. Digamos que eu tive aulas denominadas Teacher, MathTeacher e HistoryTeacher. Agora MathTeacher e HistoryTeacher se estendem do Teacher, mas e se quisermos uma classe que represente alguém que seja um MathTeacher e HistoryTeacher. Pode ficar bastante confuso ao tentar herdar de várias classes quando você pode fazê-lo apenas uma de cada vez (existem maneiras, mas elas não são exatamente ideais). Com interfaces, você pode ter 2 interfaces chamadas IMathTeacher e IHistoryTeacher e, em seguida, ter uma classe que se estende do Teacher e implementa essas 2 interfaces.
Uma desvantagem do uso de interfaces é que, às vezes, vejo pessoas duplicar código (já que você precisa criar a implementação para cada classe, implementar a interface), no entanto, existe uma solução limpa para esse problema, por exemplo, o uso de coisas como delegados (não tenho certeza qual o Java equivalente a isso).
O principal motivo para usar interfaces sobre heranças é a dissociação do código de implementação, mas não pense que a herança seja ruim, pois ainda é muito útil.
fonte
Se você herdar de uma classe, terá uma dependência não apenas dessa classe, mas também precisará honrar quaisquer contratos implícitos que tenham sido criados pelos clientes dessa classe. O tio Bob fornece um ótimo exemplo de como é fácil violar sutilmente o Princípio de Substituição de Liskov usando o dilema básico de Retângulo estendido do quadrado . As interfaces, uma vez que não têm implementação, também não têm contratos ocultos.
fonte