¿Cuándo usar la herencia, cuándo usar 'solo un campo booleano'?

18

En nuestra aplicación Rails, estamos agregando notificaciones. Algunos de estos son blocking: detienen el progreso de cualquier recurso en el que se agreguen, porque falta información sobre ese recurso.

Otras notificaciones son notificaciones simples y solo proporcionan información.

Hoy tuve una discusión con otro programador de nuestro equipo. He creado la estructura de herencia de esta manera:

ingrese la descripción de la imagen aquí

Sin embargo, prefiere que solo agregue blockingcomo un método de retorno booleano en cada Notificación y especifique una lista de subclases que están bloqueando dentro de la clase principal de Notificación.

La diferencia entre estos enfoques no es muy grande; En mi enfoque, uno no tiene que especificar esta lista, manteniendo limpia la clase raíz. Por otro lado, la lógica especial que ocurre en Notification::Blockingeste momento tampoco es muy grande.

¿Qué tipo de abstracción es más adecuada para este problema?

Qqwy
fuente
11
Una clase de padres nunca debe saber acerca de sus hijos. ¿Por qué necesitas mantener una lista de subclases?
coteyr
¿Cómo se agregan a un recurso y cómo detienen su progreso?
nulo
3
¿Por qué necesitas tantas clases de notificación? Me parece que podría crear una clase de notificación y luego dejar que los datos impulsen las acciones en lugar de los tipos de datos.
Trispedó
1
@Trisped: correcto, si te encuentras moviendo un aspecto del comportamiento a la clase base con una lista exhaustiva de casos, entonces ten el coraje de tus convicciones, admite que realmente no estás diseñando una clase base utilizable para la personalización por extensión , y mover todo el comportamiento a la clase base!
Steve Jessop

Respuestas:

35

Desea evitar que las clases base conozcan las clases derivadas. Presenta un acoplamiento estrecho y es un dolor de cabeza de mantenimiento porque debe recordar agregar a la lista cada vez que cree una nueva clase derivada.

También evitará que pueda colocar la clase de Notificación en un paquete / ensamblaje reutilizable si desea utilizar esta clase en múltiples proyectos.

Si realmente desea utilizar una sola clase base, otra forma de resolver esto es agregar una propiedad virtual o método IsBlocking en la clase de notificación base. Las clases derivadas podrían anular eso para devolver verdadero o falso. Tendría una solución de clase única sin que la clase base conozca las clases derivadas.

17 de 26
fuente
3
Esta. Toma decisiones en un solo lugar. No difunda el conocimiento de qué clases bloquean entre la clase y la lista.
candied_orange
Este es el que hago, funciona muy bien y es muy reutilizable.
coteyr
13

y especifique una lista de subclases que están bloqueando dentro de la clase primaria Notificación.

Eso se ve muy peculiar y es un código de olor particular.

Proporcionaría subclases si tiene diferencias de comportamiento entre las clases y desea tratar todas estas notificaciones de la misma manera (es decir, utilizando polimorfismo ).

Brian Agnew
fuente
1
Creo que el "comportamiento" es la clave aquí: cuando se trata solo de datos, el campo debería ser un discriminador suficiente. El comportamiento es la mejor razón para usar el polimorfismo, sin embargo, siempre se debe considerar la complejidad del mantenimiento al crear jerarquías de herencia. Leer en.wikipedia.org/wiki/Composition_over_inheritance
cottsak
7

Como respuesta a las respuestas existentes, sugeriría que la propiedad booleana es la mejor opción si se requiere cambiar dinámicamente el modo que se utilizará (por ejemplo, a través de un archivo de configuración que proporciona una lista de los tipos que se van a bloquear y que no lo son).

Dicho esto, un mejor diseño incluso en esta situación podría ser usar un objeto Decorador.

Jules
fuente
1

Diría que depende de cuánto más sea especial acerca de una notificación de bloqueo, aunque mi primer pensamiento es ir con "ambos":

class Notification
 virtual Boolean Blocking{get return false;}

class BlockingNotification inherits Notification
 virtual overrides Boolean Blocking{get return true;}

De esa manera, puede usar n.Blockingo n is BlockingNotification(todo en pseudocódigo), aunque, si va a permitir que una clase implemente un Blockingvalor sensible al contexto , ya que tendría que verificar ese valor cada vez, la BlockingNotificationclase se convierte en Menos útil.

En cualquier caso, estoy de acuerdo con las otras respuestas que no desea que la implementación de la clase base Blockingtenga que saber sobre las clases derivadas.

Mark Hurd
fuente
0

En lugar de hacer dos clases base y múltiples instancias de cada una, haga una clase de notificación con un bool para indicar si la notificación está bloqueando y cualquier otra información necesaria para comunicar la notificación al usuario.

Esto le permite usar un conjunto de código para procesar y presentar notificaciones y reduce la complejidad de su código.

Trisped
fuente