Quand utiliser l'héritage, quand utiliser «juste un champ booléen»?

18

Dans notre application Rails, nous ajoutons des notifications. Certains d'entre eux sont blocking: Ils arrêtent la progression de la ressource sur laquelle ils sont ajoutés, car certaines informations sur cette ressource sont manquantes.

Les autres notifications sont de simples notifications et ne fournissent que des informations.

Aujourd'hui, j'ai eu une discussion avec un autre programmeur de notre équipe. J'ai créé la structure d'héritage comme ceci:

entrez la description de l'image ici

Cependant, il préfère que j'ajoute simplement blockingune méthode de retour booléen sur chaque notification et spécifie une liste de sous-classes qui bloquent la classe parent Notification.

La différence entre ces approches n'est pas très grande; dans mon approche, il n'est pas nécessaire de spécifier cette liste, en gardant la classe racine plus propre. D'un autre côté, la logique spéciale qui se produit Notification::Blockingactuellement n'est pas très grande non plus.

Quel type d'abstraction convient le mieux à ce problème?

Qqwy
la source
11
Une classe de parents ne devrait jamais connaître ses enfants. Pourquoi avez-vous besoin de conserver une liste de sous-classes?
coteyr
Comment sont-ils ajoutés à une ressource et comment arrêtent-ils sa progression?
null
3
Pourquoi avez-vous besoin de tant de classes de notification? Cela me semble que vous pourriez créer une classe de notification, puis laisser les données conduire les actions au lieu des types de données.
Trispé
1
@Trisped: à droite, si vous vous retrouvez à déplacer un aspect du comportement dans la classe de base avec une liste exhaustive de cas, alors ayez le courage de vos convictions, admettez que vous ne concevez pas vraiment une classe de base utilisable pour la personnalisation par extension , et déplacez tout le comportement dans la classe de base!
Steve Jessop

Réponses:

35

Vous voulez éviter que les classes de base connaissent les classes dérivées. Il introduit un couplage étroit et est un casse-tête de maintenance car vous devez vous rappeler d'ajouter à la liste chaque fois que vous créez une nouvelle classe dérivée.

Cela vous empêchera également de pouvoir placer la classe Notification dans un package / assembly réutilisable si vous souhaitez utiliser cette classe dans plusieurs projets.

Si vous voulez vraiment utiliser une seule classe de base, une autre façon de résoudre ce problème consiste à ajouter une propriété ou une méthode virtuelle IsBlocking sur la classe Notification de base. Les classes dérivées pourraient alors remplacer cela pour retourner vrai ou faux. Vous auriez une solution à classe unique sans que la classe de base ne connaisse les classes dérivées.

17 sur 26
la source
3
Cette. Prenez des décisions en un seul endroit. Ne dispersez pas la connaissance des classes bloquées entre la classe et la liste.
candied_orange
C'est celui que je fais, qui fonctionne très bien et est très réutilisable.
coteyr
13

et spécifiez une liste de sous-classes qui bloquent l'intérieur de la classe parent Notification.

Cela semble très particulier et est une odeur de code particulière.

Je fournirais des sous-classes si vous avez des différences de comportement entre les classes et que vous souhaitez traiter toutes ces notifications de la même manière (c'est-à-dire en utilisant le polymorphisme ).

Brian Agnew
la source
1
Je pense que le "comportement" est la clé ici: quand ce ne sont que des données, le champ doit être un discriminateur suffisant. Le comportement est la meilleure raison d'utiliser le polymorphisme, mais il faut toujours tenir compte de la complexité de la maintenance lors de la création de hiérarchies d'héritage. Lire en.wikipedia.org/wiki/Composition_over_inheritance
cottsak
7

À l'inverse des réponses existantes, je suggérerais que la propriété booléenne est la meilleure option si le mode à utiliser doit être changé dynamiquement (par exemple via un fichier de configuration qui donne une liste des types à bloquer). et qui ne le sont pas).

Cela dit, une meilleure conception, même dans cette situation, pourrait être d'utiliser un objet Decorator.

Jules
la source
1

Je dirais que cela dépend de ce qui est spécial à propos d'une notification de blocage, bien que ma première pensée soit d'aller avec "les deux":

class Notification
 virtual Boolean Blocking{get return false;}

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

De cette façon, vous pouvez utiliser n.Blockingou n is BlockingNotification(tout en pseudo-code), bien que, si vous allez autoriser une classe à implémenter une Blockingvaleur contextuelle , vu que vous devrez vérifier cette valeur à chaque fois, la BlockingNotificationclasse devient moins utile.

Dans tous les cas, je suis d'accord avec les autres réponses selon lesquelles vous ne voulez pas que l'implémentation de la classe de base Blockingconnaisse les classes dérivées.

Mark Hurd
la source
0

Au lieu de créer deux classes de base et plusieurs instances de chacune, créez une classe de notification avec un booléen pour indiquer si la notification est bloquante et toute autre information nécessaire pour communiquer la notification à l'utilisateur.

Cela vous permet d'utiliser un seul ensemble de codes pour le traitement et la présentation des notifications et réduit la complexité de votre code.

Trisped
la source