Por que evitar a herança de Java “Extends”

39

Jame Gosling disse

“You should avoid implementation inheritance whenever possible.”

e, em vez disso, use a herança de interface.

Mas por quê? 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, por favor, dar um exemplo orientado a objetos ilustrando esse conceito em um cenário como "encomendar um livro em uma livraria?"

    
por newbie 10.05.2011 / 16:00
fonte

6 respostas

37

Gosling não disse para evitar o uso de palavra-chave extendida ... ele apenas disse para não usar a herança como um meio de obter reutilização de código.

Herança não é ruim por si só e é uma ferramenta muito poderosa (essencial) para usar na criação de estruturas OO. No entanto, quando não usado corretamente (que é quando usado para algo else que criar estruturas Object), 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 facilmente com interfaces, portanto, a instrução usa interfaces ao invés de classes ao projetar suas Estruturas de Objetos. Se você se encontrar usando extensões como um meio de evitar copiar e colar código de um objeto para outro, talvez esteja usando errado e seria melhor servido com uma classe especializada para lidar com essa funcionalidade e compartilhar esse código por meio da composição.

Leia sobre o Princípio de substituição de Liskov e entenda-o. Sempre que você estiver prestes a estender uma aula (ou uma interface para esse assunto), pergunte a si mesmo se você está violando o princípio, se sim, então você está mais propenso (ab) a usar a herança de maneiras antinaturais. É fácil de fazer e muitas vezes parece ser a coisa certa, mas tornará seu código mais difícil de manter, testar e evoluir.

--- Editar Esta resposta é um bom argumento para responder à questão em termos mais gerais.

    
por 10.05.2011 / 16:26
fonte
9

Nos primórdios da programação orientada a objetos, a herança de implementação era o golpe de ouro da reutilização de código. Se houvesse uma funcionalidade em alguma classe que você precisasse, a coisa a fazer era herdar publicamente dessa classe (esses eram os dias em que o C ++ era mais predominante do que o Java), e você obtinha essa funcionalidade "de graça". / p>

Exceto, não foi realmente livre. O resultado foi 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 manipular. Isso é parte da razão pela qual os projetistas do Java optaram por não suportar múltiplas heranças de classes.

O conselho de Gosling (e outras declarações) era strong remédio para uma doença bastante séria, e é possível que alguns bebês tenham sido expelidos com a água do banho. Não há nada errado em usar a herança para modelar um relacionamento entre classes que realmente é is-a . Mas se você estiver apenas procurando usar uma funcionalidade de outra classe, será melhor investigar outras opções antes.

    
por 10.05.2011 / 16:33
fonte
6

A herança reuniu uma reputação assustadora ultimamente. Uma razão para evitá-lo vai junto com o princípio do design de "composição sobre herança", que basicamente encoraja as pessoas a não usarem herança onde essa não é a verdadeira relação, apenas para salvar alguns códigos escritos. Esse tipo de herança pode dificultar a localização e dificultar a correção de bugs. A razão pela qual seu amigo provavelmente estava sugerindo usar uma interface é porque as interfaces fornecem uma solução mais flexível e sustentável para as APIs distribuídas. Eles costumam permitir 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.

    
por 10.05.2011 / 16:14
fonte
5

Porque você só pode estender uma classe, enquanto você pode implementar muitas interfaces.

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.

    
por 10.05.2011 / 16:12
fonte
1

Também aprendi isso e prefiro interfaces sempre que possível (claro que ainda uso herança onde faz sentido).

Uma coisa que eu acho que faz é dissociar 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 gravar no console. Agora vamos dizer que eu quero mudar para a impressão de uma janela GUI. Bem, agora eu tenho que modificar o método ou escrever um novo que use o GUIWriter como parâmetro. Se eu comecei definindo uma interface IWriter e peguei o método em um IWriter eu 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 só teria que mudar a classe que está sendo passada.

Outra coisa (que vale para o C #, não tenho certeza sobre o Java) é que você só pode estender uma classe, mas implementar muitas interfaces. Vamos dizer que eu tive uma classe chamada Teacher, MathTeacher e HistoryTeacher. Agora MathTeacher e HistoryTeacher se estendem do professor, mas e se quisermos uma classe que represente alguém que seja tanto MathTeacher quanto HistoryTeacher. Pode ficar bastante confuso quando tentar herdar de várias classes, quando você só pode fazê-lo uma de cada vez (existem maneiras, mas elas não são exatamente ótimas). Com as interfaces, você pode ter duas interfaces chamadas IMathTeacher e IHistoryTeacher e, em seguida, ter uma classe que se estenda do Teacher e implemente essas duas interfaces.

Uma desvantagem de usar interfaces é que às vezes vejo pessoas duplicando código (já que você precisa criar a implementação para cada classe que implementa a interface); no entanto, há uma solução limpa para esse problema, por exemplo, o uso de itens como delegados (não sei o que é o equivalente a Java).

O principal motivo para usar interfaces sobre heranças é o desacoplamento do código de implementação, mas não pense que a herança é ruim, pois ainda é muito útil.

    
por 10.05.2011 / 16:30
fonte
0

Se você herdar de uma classe, você terá uma dependência não apenas dessa classe, mas também terá que 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 Square estende o retângulo . Interfaces, uma vez que não têm implementação, também não têm contratos ocultos.

    
por 10.05.2011 / 17:10
fonte