¿Debería una clase saber sobre sus subclases?

24

¿Debería una clase saber sobre sus subclases? ¿Debería una clase hacer algo específico para una subclase dada, por ejemplo?

Mis instintos me dicen que es un mal diseño, parece un antipatrón de algún tipo.

m4design
fuente
66
Generalmente no, pero en casos especiales es útil.
CodesInChaos
Que es un anti-patrón, un conocido que se llama: "principio de sustitución de liskov" o, a veces simplemente LSP, leer sobre él en en.wikipedia.org/wiki/Liskov_substitution_principle
Jimmy Hoffa
55
@JimmyHoffa - "Principio de sustitución de Liskov" no es el nombre de un antipatrón. Un antipatrón puede violar el LSP, pero ni siquiera es seguro que esto sea cierto aquí. Es posible que, incluso si un tipo sabe que son subtipos, los subtipos puedan ser sustituidos por su padre con contravarianza y covarianza.
Matthew Flynn el
44
@MatthewFlynn Tal vez estoy usando LSP un poco flojo, pero en mi definición no es el hecho de que todos cumplan con los requisitos de los demás que cumplan con LSP, es un requisito que tengan un desacoplamiento tal que no solo estén de acuerdo entre ellos sino podría implementar otra subclase que no viole LSP. Si la jerarquía es consciente de sí misma, entonces una implementación externa no podría cumplir con LSP sin alterar la clase base. Quizás eso no sea estrictamente LSP, pero está muy cerca. y sí, debería haber dicho que la violación de LSP es el antipatrón
Jimmy Hoffa
2
Las pocas veces que me he encontrado con esto, refactoricé ese conocimiento en un método que podría ser anulado en una subclase (forzado por hacerlo abstracto o virtual puro, o proporcionando una implementación predeterminada sensible que podría ser anulada).

Respuestas:

32

La respuesta que implica el concepto de clases es "no".

Cualquiera que sea la acción, los datos o la relación que esté manejando es parte de todas las subclases, entonces debe manejarse en la superclase sin verificar el tipo real. O se aplica solo a algunas subclases: entonces tendría que realizar verificaciones de tipo en tiempo de ejecución para hacer lo correcto, la superclase tendría que cambiarse cada vez que alguien más herede de ella (o podría hacer silenciosamente lo incorrecto), los cambios en las clases derivadas pueden romper la superclase sin cambios, etc.

En resumen, obtienes una serie de malas consecuencias que generalmente son lo suficientemente malas como para rechazar tales soluciones de inmediato. Si varias de sus subclases hacen lo mismo y desea evitar la duplicación de código (prácticamente siempre es algo bueno), una mejor solución es introducir una clase de nivel medio de la que todas esas subclases puedan heredar el código.

Kilian Foth
fuente
44
Una excepción a esta regla: la documentación de la clase debe enumerar sus subclases locales en su propia biblioteca.
Kieveli
3
@Kieveli ... siempre y cuando esa documentación se mantenga actualizada.
Mouviciel
14
Cualquier herramienta de documentación utilizable debería ser capaz de dibujar árboles de herencia ...
johannes
13
No creo que sea una excepción. La clase todavía no sabe nada. La documentación lo sabe.
Honza Brabec
44
@Kieveli uhh, estoy completamente en desacuerdo.
Jimmy Hoffa
16

¡No solo no debería saberlo, simplemente no puede ! Por lo general, una clase se puede extender en cualquier momento y en cualquier lugar. Puede ser extendido por clases que ni siquiera existían cuando fue escrito.

Algunos lenguajes permiten que las clases extendidas sean controladas por la superclase. En Scala, una clase puede marcarse como sealed, lo que significa que solo puede ser extendida por otras clases dentro de la misma unidad de compilación (archivo fuente). Sin embargo, a menos que esas subclases también sean sealedo final, las subclases pueden ser extendidas por otras clases.

En Scala, esto se usa para modelar tipos de datos algebraicos cerrados, por lo que el Listtipo canónico de Haskell :

data List a = Nil | Cons a (List a)

se puede modelar en Scala así:

sealed trait List[+A]

case object Nil extends List[Nothing]

final case class Cons[+A] extends List[A]

Y puede garantizar que solo existen esas dos subclases porque Listes sealedy, por lo tanto, no puede extenderse fuera del archivo, Conses finaly, por lo tanto, no puede extenderse en absoluto y Niles una objectque no puede extenderse de todos modos.

Pero este es un caso de uso específico (modelado de tipos de datos algebraicos a través de la herencia) e incluso en este caso, la superclase en realidad no conoce sus subclases. Es más una garantía para el usuario del Listtipo que si hace una discriminación de caso Nily Cons, no habrá ninguna otra alternativa apareciendo a sus espaldas.

Jörg W Mittag
fuente
Lo que funciona en muchos idiomas es agregar alguna forma de abstract internalmiembro.
CodesInChaos
4

La respuesta simple es no.

Hace que el código sea frágil y da a conocer dos principios básicos de la programación orientada a objetos.

  • Principio de sustitución de Liskov
  • Principio abierto cerrado
Sajad Deyargaroo
fuente
1

Sí a veces. Por ejemplo, cuando existe un número limitado de subclases. Un patrón de visitante es una ilustración de la utilidad de este enfoque.

Ejemplo: todos los nodos de árbol de sintaxis abstracta (AST) de una gramática bien definida pueden heredarse de una sola Nodeclase que implementa un patrón de visitante para manejar todos los tipos de nodos.

lorus
fuente
99
No, no y no Esto no es útil y nunca es una buena solución.
Sulthan
@Sulthan He trabajado con AST fuera del aula y creo que lorus realmente no entiende la pregunta. Un visitante no es un tipo de nodo en un AST, es un objeto separado. Puede y debe saber sobre los objetos gramaticales. Pero un nodo AST de alto nivel no debería.
1
@JohnGaughan El término "sabe" me confunde. La Nodeclase base , por supuesto, no contiene ninguna referencia directa a sus subclases. Pero contiene un acceptmétodo con un Visitorparámetro. Y Visitorcontiene un visitmétodo por cada subclase. Entonces, a pesar de Nodeno tener referencias directas a sus subclases, "las conoce" indirectamente, a través de la Visitorinterfaz. Todos se unieron a través de él.
lorus
1
@Sulthan Nunca digas nunca. El tipo de opción es un ejemplo válido del caso cuando supreclass sabe sobre el descendiente. Pero como dijo Jorg, se marcaría como sellado.
Pavel Voronin
Entonces están de acuerdo: un visitante necesita saber lo que está visitando.
1

Si escribo un componente para una empresa y luego, después de que me vaya, alguien lo extiende para sus propios fines, ¿debo informarme al respecto?

¡No!

Lo mismo con las clases. Confía en tus instintos.

Daniel Hollinrake
fuente
¿¿¿Seguridad en el empleo???
sixtyfootersdude
Tu altura de sesenta pies, así que no discutiré contigo :-) Sin embargo, espero que mi significado fue después de que me fui y obtuve otro trabajo.
Daniel Hollinrake