Pregunta

En Java, ¿existe alguna vez un caso para permitir que se extienda una clase no abstracta?

Siempre parece indicar un código incorrecto cuando hay jerarquías de clases.¿Estás de acuerdo y por qué/por qué no?

¿Fue útil?

Solución

Estoy de acuerdo con Jon y Kent, pero, como Scott Myers (en efectivo de C ++), que vaya mucho más allá. Creo que cada clase debe ser abstract, o final . Es decir, sólo las clases de hojas en ninguna jerarquía son muy aptos para la instanciación directa. Todas las otras clases (es decir, los nodos interiores en la herencia) son “terminar” y debe por consiguiente ser <=>.

Simplemente no tiene sentido para las clases habituales para extenderse más allá. Si un aspecto de la clase vale la pena ampliar y / o modificar, la forma más limpia sería tomar una clase y que separarlo en una <=> clase base y una implementación concreta intercambiables.

Otros consejos

Es cierto que hay momentos en los que tiene sentido tener clases concretas no finales. Sin embargo, estoy de acuerdo con Kent - Creo que las clases deben ser definitiva (sellado en C #) por defecto, y que los métodos de Java debería ser final por defecto (como en C #)

.

Como dice Kent, la herencia requiere un cuidadoso diseño y documentación - es muy fácil pensar que sólo puede anular un solo método, pero no conocer las situaciones en las que este método se puede llamar a la clase base como parte del resto de la aplicación.

"¿Cómo se diseña una clase de herencia" para más discusión sobre esto.

Esta pregunta es igualmente aplicable a otras plataformas como C # .NET. Hay aquellos (me incluyo) que creen tipos deben ser definitiva / sellado por defecto y deben ser sin sellar de forma explícita para permitir la herencia.

Extensión a través de la herencia es algo que necesita un diseño cuidadoso y no es tan simple como dejar un tipo sin sellar. Por lo tanto, yo creo que debería ser una decisión explícita para permitir la herencia.

Hay una buena razón para mantener su código no final. muchos marcos como Hibernate, primavera, guice dependen veces en clases no finales que se extiende de forma dinámica en tiempo de ejecución.

por ejemplo, de hibernación utiliza proxies para recuperación perezosa de asociaciones. sobre todo cuando se trata de AOP, tendrá que sus clases no final, de manera que los interceptores pueden adherirse a ella. véase también la pregunta al SO

Su mejor referencia aquí es la casilla 15 del excelente libro de Joshua Bloch "Effective Java", denominado "Diseño y documentos para la herencia o bien prohibirla". Sin embargo, la clave para saber si la extensión de una clase se debe permitir no es "¿es abstracto" pero "fue diseñado con la herencia en cuenta". A veces hay una correlación entre los dos, pero es el segundo que es importante. Para tomar un ejemplo simple mayoría de las clases de AWT están diseñados para extenderse, incluso los que no son abstractos.

El resumen del capítulo de Bloch es que la interacción de las clases heredadas de sus padres puede ser sorprendente e impredecible si el ancestro no fue diseñado para ser heredado de. Por lo tanto, las clases deben ser de dos clases diseñadas para ser extendido, y con suficiente documentación de tipo A) para describir la forma en que debe hacerse b) clases marcados final. Las clases en (a) serán a menudo abstracto, pero no siempre. Para

En algunos casos querrá asegurarse de que no hay subclases, en otros casos, usted quiere asegurarse de subclases (resumen). Pero siempre hay un gran subconjunto de clases en las que como el autor original no se preocupan y no debe preocuparse. Es parte de ser abierta / cerrada. La decisión de que algo debe estar cerrada es también para ser hecho por una razón.

No estoy de acuerdo. Si jerarquías eran malos, no habría ninguna razón para lenguajes orientados a objetos que existen. Si nos fijamos en las bibliotecas del aparato UI de Microsoft y Sun, que está seguro de encontrar la herencia. Es que todos los "código malo" por definición? No, por supuesto que no.

La herencia puede ser objeto de abuso, pero también puede hacerlo cualquier característica del lenguaje. El truco es aprender cómo hacer las cosas adecuadamente.

No podría estar más en desacuerdo.Las jerarquías de clases tienen sentido para clases concretas cuando las clases concretas conocen los posibles tipos de retorno de métodos que no han marcado como finales.Por ejemplo, una clase concreta puede tener un gancho de subclase:

protected SomeType doSomething() {
return null;
}

Se garantiza que este doSomething será nulo o una instancia de SomeType.Digamos que tiene la capacidad de procesar la instancia SomeType pero no tiene un caso de uso para usar la instancia SomeType en la clase actual, pero sepa que sería realmente bueno tener esta funcionalidad en subclases y casi todo es concreto.No tiene sentido convertir la clase actual en una clase abstracta si se puede usar directamente con el valor predeterminado de no hacer nada con su valor nulo.Si la convirtieras en una clase abstracta, entonces tendrías sus hijos en este tipo de jerarquía:

  • Clase base abstracta
    • Clase predeterminada (la clase que podría haber sido no abstracta, solo implementa el método protegido y nada más)
    • Otras subclases.

Por lo tanto, tiene una clase base abstracta que no se puede usar directamente, cuando la clase predeterminada puede ser el caso más común.En la otra jerarquía, hay una clase menos, por lo que la funcionalidad se puede usar sin crear una clase predeterminada esencialmente inútil porque simplemente hubo que forzar la abstracción en la clase.

  • Clase predeterminada
    • Otras subclases.

Ahora bien, claro, se puede usar y abusar de las jerarquías, y si las cosas no están documentadas claramente o las clases no están bien diseñadas, las subclases pueden tener problemas.Pero estos mismos problemas también existen con las clases abstractas, no te deshaces del problema solo porque agregas "abstracto" a tu clase.Por ejemplo, si el contrato del método "doSomething()" anterior requería que SomeType hubiera completado los campos x, y, z cuando se accedía a ellos a través de captadores y definidores, su subclase explotaría independientemente de si usaba la clase concreta que devolvía nulo. como su clase base o una clase abstracta.

La regla general para diseñar una jerarquía de clases es prácticamente un cuestionario simple:

  1. ¿Necesito el comportamiento de mi superclase propuesta en mi subclase?(S/N) Esta es la primera pregunta que debes hacerte.Si no necesita el comportamiento, no hay argumentos para subclasificar.

  2. ¿Necesito el estado de mi superclase propuesta en mi subclase?(S/N) Esta es la segunda pregunta.Si el estado se ajusta al modelo de lo que necesita, este puede ser un candidato para la subclasificación.

  3. Si la subclase se creara a partir de la superclase propuesta, ¿sería realmente una relación IS-A o es simplemente un atajo para heredar comportamiento y estado?Esta es la última pregunta.Si es sólo un atajo y no puede calificar la subclase propuesta como "una" superclase, entonces se debe evitar la herencia.El estado y la lógica se pueden copiar y pegar en la nueva clase con una raíz diferente, o se puede utilizar la delegación.

Solo si una clase necesita el comportamiento, el estado y se puede considerar que la subclase ES-UNA(n) instancia de la superclase, se debe considerar que hereda de una superclase.De lo contrario, existen otras opciones que se adaptarían mejor al propósito, aunque puede requerir un poco más de trabajo desde el principio, a la larga es más limpio.

Existen algunos casos en los que no queremos para permitir cambiar el comportamiento. Por ejemplo, la clase String, Math.

No me gusta herencia porque siempre hay una forma mejor de hacer lo mismo, pero cuando estás haciendo cambios de mantenimiento en un enorme sistema a veces la mejor manera de solucionar el código con cambios mínimos es extender una clase un poco . Sí, por lo general conduce a un mal código, sino a uno que funciona y sin meses de volver a escribir primero. Así que dar un hombre de mantenimiento de la mayor flexibilidad que puede manejar es una buena manera de ir.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top