Pergunta

Em Java, há sempre um caso para permitir que uma classe não-abstrata para ser estendida?

É sempre parece indicar código ruim quando há hierarquias de classe. Você concorda, e por quê / por que não?

Foi útil?

Solução

Eu concordo com Jon e Kent, mas, como Scott Myers (em Effective C ++), I ir muito mais longe. Acredito que todas as classes devem ser abstract, ou final . Ou seja, apenas as classes folha em qualquer hierarquia são realmente apto para instanciação direta. Todas as outras classes (ou seja, nós internos na herança) são “inacabado”, pelo que devem ser abstract.

Ele simplesmente não faz sentido para as classes habituais de ser novamente prorrogado. Se um aspecto da classe vale a pena estender e / ou modificação, a maneira mais limpa seria a de tomar aquela classe e separá-lo em uma classe base abstract e uma implementação intercambiáveis ??concreto.

Outras dicas

Há certamente momentos em que faz sentido ter classes concretas não-finais. No entanto, concordo com Kent - Eu acredito que as aulas devem ser final (selado em C #) por padrão, e que métodos Java deve ser final por padrão (como eles estão em C #)

.

Como Kent diz, herança requer uma concepção e documentação cuidadosa - é muito fácil pensar que você pode simplesmente substituir um único método, mas não sei as situações em que esse método pode ser chamado a partir da classe de base como parte do resto do implementação.

"Como você projeta uma classe para herança" para mais discussão sobre este assunto.

Esta questão é igualmente aplicável a outras plataformas, como C # .NET. Existem aqueles (eu incluído) que acreditam tipos deve ser final / selado por padrão e precisa ser explicitamente não selada para permitir herança.

Extensão através de herança é algo que precisa um projeto cuidadoso e não é tão simples como apenas deixando um tipo selada. Portanto, eu acho que deveria ser uma decisão explícita para permitir herança.

Há umas boas razões para manter seu código não-final. muitas estruturas, como Hibernate, Spring, Guice dependem às vezes em classes não-finais que se estende de forma dinâmica em tempo de execução.

Por exemplo, hibernação usa proxies para buscar associação preguiçoso. especialmente quando se trata de AOP, você vai querer suas classes não-final, de modo que os interceptores pode anexar a ele. ver também a questão no SO

Seu melhor referência aqui é item 15 do excelente livro de Joshua Bloch "Effective Java", chamada "Projeto e documento de herança ou então proibi-la". No entanto, a chave para saber se a extensão de uma classe deve ser permitida não é "é abstrato", mas "foi projetado com herança em mente". Por vezes, existe uma correlação entre os dois, mas é o segundo que é importante. Para tomar um exemplo simples a maioria das classes AWT são projetados para ser estendido, mesmo aqueles que não são abstratos.

O resumo do capítulo de Bloch é que a interação de classes herdadas com seus pais pode ser surpreendente e imprevisíveis se o ancestral não foi projetado para ser herdada de. As aulas devem, portanto, vêm em dois tipos a) As categorias concebidas para ser estendido, e com documentação suficiente para descrever como deve ser b) aulas done marcados final. Classes em (a) será muitas vezes abstrato, mas nem sempre. Para

Em alguns casos, você quer ter certeza de que não há subclasses, em outros casos você quiser garantir subclasse (abstrato). Mas há sempre um grande subconjunto de classes onde você como o autor original não se importa e não deve se preocupar. É parte de ser aberto / fechado. Decidir que algo deve ser fechado é também a ser feito por uma razão.

Eu discordo. Se hierarquias eram ruins, não haveria razão para linguagens orientadas a objeto de existir. Se você olhar para UI bibliotecas da Microsoft e Sun widget, você está certo para encontrá herança. isso é tudo "código ruim" por definição? Não, claro que não.

A herança pode ser abusado, mas também pode ser qualquer recurso de linguagem. O truque é aprender como fazer as coisas de forma adequada.

Eu não poderia discordar mais. hierarquias de classe faz sentido para classes concretas quando as classes concretas conhecer os possíveis tipos de retorno de métodos que não foram marcadas final. Por exemplo, uma classe concreta pode ter um gancho de subclasse:

protected SomeType doSomething() {
return null;
}

Esta doSomething é guarenteed ser nulo ou uma instância SomeType. Dizer que você tem a capacidade de processar a instância SomeType, mas não têm um caso de uso para usar a instância SomeType na classe atual, mas sei que esta funcionalidade seria muito bom ter em subclasses e mais tudo é concreto. Não faz sentido para fazer a classe atual uma classe abstrata, se ele pode ser usado diretamente com o padrão de não fazer nada com o seu valor nulo. Se você fez uma classe abstrata, então você teria que suas crianças neste tipo de hierarquia:

  • classe base Abstract
    • classe padrão (a classe que poderia ter sido não-abstrato, única implementa o método protegido e nada mais)
    • Outras subclasses.

Você, assim, ter uma classe base abstrata que não pode ser usado diretamente, quando a classe padrão pode ser o caso mais comum. Na outra hierarquia, há uma menor classe, para que a funcionalidade pode ser usada sem fazer uma classe padrão essencialmente inútil porque abstração só tinha de ser forçado para a classe.

  • classe Padrão
    • Outras subclasses.

Agora, com certeza, hierarquias pode ser usado e abusado, e se as coisas não são documentados de forma clara ou classes não bem concebido, subclasses pode executar em problemas. Mas esses mesmos problemas existem com classes abstratas, bem, você não se livrar do problema só porque você adicionar "abstrato" para sua classe. Por exemplo, se o contrato do "doSomething ()" método acima SomeType obrigados a ter povoada x, y e campos z quando eles foram acessados ??via getters e setters, sua subclasse iria explodir, independentemente se você usou a classe concreta que nula voltou como sua classe base ou uma classe abstrata.

A regra geral para a criação de uma hierarquia de classes é muito bonito um questionário simples:

  1. Eu preciso o comportamento do meu superclasse proposto no meu subclasse? (Y / N) Esta é a primeira pergunta que você precisa perguntar a si mesmo. Se você não precisa do comportamento, não há nenhum argumento para subclassificação.

  2. Eu preciso do meu estado de superclasse proposto no meu subclasse? (Y / N) Esta é a segunda pergunta. Se o estado se encaixa no modelo do que você precisa, este pode ser um canidate para subclassificação.

  3. Se a subclasse foi criado a partir da superclasse proposto, seria realmente ser um IS-A relação, ou é apenas um atalho para o comportamento herdar e Estado? Esta é a pergunta final. Se é apenas um atalho e você não pode qualificar sua subclasse proposta "como-a" superclasse, então a herança deve ser evitado. O estado e lógica pode ser copiado e colado na nova classe com uma raiz diferente, ou delegação pode ser usado.

Apenas se uma classe precisa do comportamento, estado e pode-se considerar que a subclasse IS-A (n) instância da superclasse deve ser considerado para herdar de uma superclasse. Caso contrário, existem outras opções que seria mais adequado para o efeito, embora possa exigir um pouco mais de trabalho na frente, é mais limpo, a longo prazo.

Existem alguns casos em que nós não queremos para permitir que para mudar o comportamento. Por exemplo, a classe String, Math.

Eu não gosto de herança, porque há sempre uma maneira melhor de fazer a mesma coisa, mas quando você está fazendo mudanças de manutenção em um enorme sistema, por vezes, a melhor maneira de corrigir o código com modificações mínimas é estender uma classe um pouco . Sim, é geralmente leva a um código ruim, mas a um grupo de trabalho e sem meses de reescrever em primeiro lugar. Então, dando um homem de manutenção tanta flexibilidade quanto ele pode lidar é um bom caminho a percorrer.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top