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


40

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?"



O problema é que você só pode estender uma classe.

Respostas:


38

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.


3
A herança (implementação) não é essencial para a criação de estruturas OO. Você pode ter um idioma OO sem ele.
Andres F.

Você poderia dar um exemplo? Eu nunca vi OO sem herança.
Newtopian

11
O problema é que não há uma definição aceita do que é OOP. (Além disso, até Alan Kay lamentou a interpretação comum de sua definição, com sua ênfase atual em "objetos" em vez de "mensagens"). Aqui está uma tentativa de resposta para sua pergunta . Concordo que é incomum ver OOP sem herança; daqui meu argumento era meramente que não é essencial ;)
Andres F.

9

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.


6

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.


5

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.


por que o voto negativo?
Mahmoud Hossam

6
A resposta coloca a carroça diante do cavalo. O Java apenas permite que você estenda uma classe por causa do princípio articulado por Gosling (designer de Java). É como dizer que as crianças devem usar capacete enquanto andam de bicicleta, porque essa é a lei. Isso é verdade, mas a lei e os conselhos decorrem da prevenção de ferimentos na cabeça.
31711 JohnMcG

@JohnMcG Não estou explicando a escolha do design que Gosling fez, estou apenas dando conselhos a um codificador, os codificadores geralmente não se preocupam com o design da linguagem.
Mahmoud Hossam

1

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.


2
"você só pode estender 1 classe, mas implementar muitas interfaces" Isso é verdade tanto para Java quanto para C #.
FrustratedWithFormsDesigner

Uma solução possível para o problema da duplicação de código é criar uma classe abstrata que implemente a interface e a estenda. Use com cuidado, no entanto; vi apenas alguns casos que fazem sentido.
Michael K

0

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.


2
Uma dica: o problema da elipse circular ainda ocorrerá mesmo que apenas interfaces puras sejam usadas, se os objetos forem mutáveis.
rwong 11/05
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.