Para las clases con campos opcionales, ¿es mejor usar herencia o una propiedad anulable? Considere este ejemplo:
class Book {
private String name;
}
class BookWithColor extends Book {
private String color;
}
o
class Book {
private String name;
private String color;
//when this is null then it is "Book" otherwise "BookWithColor"
}
o
class Book {
private String name;
private Optional<String> color;
//when isPresent() is false it is "Book" otherwise "BookWithColor"
}
El código que depende de estas 3 opciones sería:
if (book instanceof BookWithColor) { ((BookWithColor)book).getColor(); }
o
if (book.getColor() != null) { book.getColor(); }
o
if (book.getColor().isPresent()) { book.getColor(); }
El primer enfoque me parece más natural, pero tal vez sea menos legible debido a la necesidad de hacer casting. ¿Hay alguna otra forma de lograr este comportamiento?
java
inheritance
class
null
Bojan VukasovicTest
fuente
fuente
Respuestas:
Depende de las circunstancias. El ejemplo específico no es realista, ya que no se llamaría a una subclase
BookWithColor
en un programa real.Pero, en general, una propiedad que solo tiene sentido para ciertas subclases solo debería existir en esas subclases.
Por ejemplo, si
Book
tienePhysicalBook
yDigialBook
como descendientes, entoncesPhysicalBook
podría tener unaweight
propiedad yDigitalBook
unasizeInKb
propiedad. PeroDigitalBook
no tendráweight
y viceversa.Book
no tendrá ninguna propiedad, ya que una clase solo debe tener propiedades compartidas por todos los descendientes.Un mejor ejemplo es mirar clases de software real. El
JSlider
componente tiene el campomajorTickSpacing
. Como solo un control deslizante tiene "marcas", este campo solo tiene sentido paraJSlider
sus descendientes. Sería muy confuso si otros componentes hermanos comoJButton
tuvieran unmajorTickSpacing
campo.fuente
Un punto importante que no parece haber sido mencionado: en la mayoría de los idiomas, las instancias de una clase no pueden cambiar de qué clase son instancias. Entonces, si tuviera un libro sin color y quisiera agregar un color, necesitaría crear un nuevo objeto si usa clases diferentes. Y luego probablemente deba reemplazar todas las referencias al objeto antiguo con referencias al nuevo objeto.
Si "libros sin color" y "libros con color" son instancias de la misma clase, entonces agregar el color o eliminar el color será un problema mucho menor. (Si su interfaz de usuario muestra una lista de "libros con color" y "libros sin color", entonces esa interfaz de usuario tendría que cambiar obviamente, pero supongo que eso es algo que debe manejar de todos modos, similar a una "lista de rojo libros "y" lista de libros verdes ").
fuente
Piense en un JavaBean (como su
Book
) como un registro en una base de datos. Las columnas opcionales sonnull
cuando no tienen valor, pero es perfectamente legal. Por lo tanto, su segunda opción:Es lo mas razonable. 1
Sin embargo, ten cuidado con cómo usas la
Optional
clase. Por ejemplo, no lo esSerializable
, lo que suele ser una característica de un JavaBean. Aquí hay algunos consejos de Stephen Colebourne :Por lo tanto, dentro de su clase debe usar
null
para representar que el campo no está presente, pero cuandocolor
sale delBook
(como tipo de retorno) debe estar envuelto con unOptional
.Esto proporciona un diseño claro y un código más legible.
1 Si tiene la intención de que un
BookWithColor
encapsule una "clase" completa de libros que tienen capacidades especializadas sobre otros libros, entonces tendría sentido usar la herencia.fuente
Optional
clase a Java para este propósito exacto. Puede que no sea OO, pero sin duda es una opinión válida.Debería usar una interfaz (no herencia) o algún tipo de mecánica de propiedad (tal vez incluso algo que funcione como el marco de descripción de recursos).
Entonces tampoco
o
Aclaración (editar):
Simplemente agregando campos opcionales en la clase de entidad
Especialmente con el contenedor opcional(editar: ver la respuesta de 4castle ) Creo que esto (agregar campos en la entidad original) es una forma viable de agregar nuevas propiedades a pequeña escala. El mayor problema con este enfoque es que podría funcionar contra el bajo acoplamiento.Imagine que su clase de libro se define en un proyecto dedicado para su modelo de dominio. Ahora agrega otro proyecto que utiliza el modelo de dominio para realizar una tarea especial. Esta tarea requiere una propiedad adicional en la clase de libro. O terminas con herencia (ver más abajo) o tienes que cambiar el modelo de dominio común para hacer posible la nueva tarea. En el último caso, puede terminar con un montón de proyectos que dependen de sus propias propiedades agregadas a la clase de libro, mientras que la clase de libro en sí depende de estos proyectos, porque no puede comprender la clase de libro sin el proyectos adicionales
¿Por qué la herencia es problemática cuando se trata de proporcionar propiedades adicionales?
Cuando veo su clase de ejemplo "Libro", pienso en un objeto de dominio que a menudo termina teniendo muchos campos y subtipos opcionales. Solo imagine que desea agregar una propiedad para libros que incluyen un CD. Ahora hay cuatro tipos de libros: libros, libros con color, libros con CD, libros con color y CD. No puede describir esta situación con herencia en Java.
Con las interfaces, evita este problema. Puede componer fácilmente las propiedades de una clase de libro específica con interfaces. La delegación y la composición harán que sea fácil obtener exactamente la clase que desea. Con la herencia, generalmente terminas con algunas propiedades opcionales en la clase que solo están allí porque una clase hermana las necesita.
Más información sobre por qué la herencia es a menudo una idea problemática:
¿Por qué los proponentes de la OOP generalmente ven la herencia como algo malo?
JavaWorld: ¿Por qué se extiende es malo?
El problema con la implementación de un conjunto de interfaces para componer un conjunto de propiedades
Cuando usa interfaces para extensión, todo está bien siempre que tenga solo un pequeño conjunto de ellas. Especialmente cuando su modelo de objetos es utilizado y extendido por otros desarrolladores, por ejemplo, en su empresa, la cantidad de interfaces crecerá. Y eventualmente terminas creando una nueva "interfaz de propiedad" oficial que agrega un método que tus colegas ya usaron en su proyecto para el cliente X para un caso de uso completamente no relacionado: Ugh.
editar: otro aspecto importante es mencionado por gnasher729 . A menudo desea agregar dinámicamente propiedades opcionales a un objeto existente. Con herencia o interfaces, tendría que recrear todo el objeto con otra clase que está lejos de ser opcional.
Cuando espere extensiones a su modelo de objetos hasta tal punto, estará mejor modelando explícitamente la posibilidad de una extensión dinámica . Propongo algo como arriba donde cada "extensión" (en este caso propiedad) tiene su propia clase. La clase funciona como espacio de nombres e identificador para la extensión. Cuando configura las convenciones de nomenclatura del paquete de esta manera, permite una cantidad infinita de extensiones sin contaminar el espacio de nombres para los métodos en la clase de entidad original.
En el desarrollo del juego, a menudo te encuentras con situaciones en las que deseas componer el comportamiento y los datos en muchas variaciones. Esta es la razón por la cual el patrón de arquitectura entidad-componente-sistema se hizo bastante popular en los círculos de desarrollo de juegos. Este es también un enfoque interesante que puede considerar, cuando espera muchas extensiones para su modelo de objetos.
fuente
PropertiesHolder
probablemente sería una clase abstracta. ¿Pero para qué sugerirías una implementacióngetProperty
ogetPropertyValue
? Los datos aún deben almacenarse en algún tipo de campo de instancia en algún lugar. ¿O usaría aCollection<Property>
para almacenar las propiedades en lugar de usar campos?Book
extensiblePropertiesHolder
por razones de claridad. Por loPropertiesHolder
general, debe ser miembro de todas las clases que necesitan esta funcionalidad. La implementación tendría unaMap<Class<? extends Property<?>>,Property<?>>
o algo como esto. Esta es la parte fea de la implementación, pero proporciona un marco realmente agradable que incluso funciona con JAXB y JPA.Si la
Book
clase es derivable sin propiedad de color como a continuaciónentonces la herencia es razonable.
fuente
color
es privado, cualquier clase derivada no debería preocuparse decolor
todos modos. Simplemente agregue algunos constructoresBook
que ignoren porcolor
completo y se configurará de manera predeterminadanull
.