Quand déplacer un champ commun dans une classe de base?

16

J'ai actuellement deux classes dérivées, Aet B, qui ont toutes deux un champ en commun et j'essaie de déterminer s'il doit remonter dans la classe de base.

Elle n'est jamais référencée à partir de la classe de base, et dire si à un moment donné, une autre classe est dérivée, Cqui n'a pas de _field1, alors le principe des "moins privilégiés" (ou quelque chose) ne serait-il pas violé s'il était?

public abstract class Base
{
    // Should _field1 be brought up to Base?
    //protected int Field1 { get; set; }
}

public class A : Base
{
    private int _field1;
}

public class B : Base
{
    private int _field1;
}

public class C : Base
{
    // Doesn't have/reference _field1
}
Sam est
la source
18
Je pense que cette question ne sait pas parce que vous ne nous avez pas donné une idée de ce que Base, A, B, Cet _field1sont. Ce sont des détails importants qui ne doivent pas être laissés de côté; Je pense que vous devriez modifier la question pour en parler.
Tanner Swett
Sur la base des réponses que je voudrais: reconstruire le véhicule pour avoir une suspension virtuelle, puis la voiture et le vélo pourraient avoir des roues et le bateau pourrait avoir une flottabilité, ce qui déplacerait l'abstraction vers le haut. Je trouve que si mon abstraction mène directement à des paramètres spécifiques, je n'ai pas déplacé le concept assez loin dans la chaîne.
Patrick Hughes
6
N'utilisez pas l'héritage de classe pour éviter la duplication de code. Utilisez-le pour hériter et étendre le comportement , c'est-à-dire le polymorphisme. Déplacez un champ commun vers une classe de base si et seulement s'il s'agit logiquement du même champ, et non pas deux informations non liées qui se trouvent partager le même nom dans leurs contextes respectifs.
Brandon
Pourquoi avez-vous une classe de base pour commencer?
jpmc26

Réponses:

34

Tout dépend du problème exact que vous essayez de résoudre.

Prenons un exemple concret: votre classe de base abstraite est Vehicleet vous avez actuellement les implémentations concrètes Bicycleet Car. Vous envisagez de passer numberOfWheelsde Bicycleet Carau véhicule. Devriez-vous faire ça? Non! Parce que tous les véhicules n'ont pas de roues. Vous pouvez déjà dire que si vous essayez d'ajouter une Boatclasse, cela va se casser.

Maintenant, si votre classe de base abstraite l'était, WheeledVehicleil est logique d'avoir la numberOfWheelsvariable membre dedans.

Vous devez appliquer la même logique à votre problème, car comme vous pouvez le voir, ce n'est pas une simple réponse oui ou non.

Pete
la source
3
On pourrait temporairement accepter que 0 est un nombre valide de roues. Cependant, vous pouvez éventuellement ajouter une roll()méthode, à quel point l'idée de sous-classe semble prémonitoire.
user949300
11
Un bateau a 0 roues. Qu'est-ce que ça casse?
D Drmmr
14
@DDrmmr Ce n'est pas qu'un bateau a 0 roues, c'est que les roues n'existent même pas comme concept pour un bateau - donc vos modèles ne devraient pas le permettre.
Peter M
10
Mon point est que l'exemple est mauvais. Il n'y a rien de mal sur le plan conceptuel avec un véhicule (qui se trouve être un bateau) indiquant qu'il n'a pas de roues.
D Drmmr
10
Ensuite, tout va en enfer lorsque vous devez stocker un pédalo.
IllusiveBrian
13

Logiquement, au-delà de placer le champ répliqué dans des sous-classes vs en commun dans la classe de base, il y a une troisième option: introduire une nouvelle sous-classe dans la hiérarchie qui a les propriétés communes entre les deux. @Pete y fait allusion sans y aller complètement.

En utilisant l'exemple de @ Pete, nous introduirions une sous-classe (éventuellement abstraite) pour les véhicules à roues qui descend de la classe de base d'origine - tandis que les deux sous-classes en descendent. Ainsi, la classe de base d'origine n'est pas polluée par les roues, mais le point commun des roues est SEC (non répété parmi les sous-classes qui ont des roues).

Bien sûr, cela peut être exagéré pour vos besoins, mais cela est pris en charge par le mécanisme de hiérarchie des classes.

Erik Eidt
la source
C'est en fait ce que j'ai décidé de faire (A et B se chevauchent principalement, donc B dérivera de A).
samis
9

Je vais jouer l'avocat du diable ici.

Pour l'instant, vous ne devriez rien faire .

Est-ce SEC? Non. Mais il vaut mieux avoir un peu de duplication qu'une abstraction prématurée dont vous ne pourrez pas facilement vous défaire plus tard. Le refactor pour déplacer une propriété vers une classe de base commune est facile. Aller dans l'autre sens ne l'est pas. Attend et regarde.

Lorsque je prends ce genre de décision, j'ai tendance à utiliser une "règle de 3": une fois que j'ai répété la même chose, par exemple à trois endroits différents, et seulement alors, je pense à le déplacer dans la chaîne. NB vous êtes seulement à 2.

Jared Smith
la source
2
Une observation sage qui est, je dirais, nécessaire pour prendre la décision.
radarbob
1
Je suis généralement d'accord, mais "en ce moment" (sans code supplémentaire comme celui que nous voyons), le refactoring dans les deux sens est trivial. Mais s'il y a du code supplémentaire qui utilise le champ, la refactorisation dans les deux sens peut devenir beaucoup plus difficile.
Doc Brown
2

En général, je le déplacerais dans la classe de base. Je ne pense pas qu'il y ait un objectif oui / non, car il y a un compromis ici - porter des champs inutilisés contre réduire la complexité.

Je préfère généralement les classes de base «lourdes» qui contiennent tout ce qui pourrait être partagé. Cela rend la sérialisation vers des fichiers plus simple car vous n'avez pas besoin de méthodes de sérialisation descendantes dans chaque classe dérivée. Mais si vous n'avez pas cela ou un problème similaire, ou peut-être devez-vous faire tout ce que vous pouvez pour réduire l'utilisation de la mémoire, ne conserver que les champs où vous en avez besoin devrait être correct.

Une classe «intermédiaire» qui présente les champs communs sera très bien si vous avez un nombre très limité de champs. Mais sachez que cette approche peut considérablement augmenter la complexité si vous avez des dizaines de champs utilisés dans différentes combinaisons, conduisant à de nombreuses classes intermédiaires introduisant chacune un ensemble spécifique de champs. Cela peut devenir un problème de maintenance.

GrandmasterB
la source
Mon exemple est trivial, bien qu'il s'agisse toujours d'un code de production. Vous faites un bon argument pour "compromettre" la hiérarchie avec une classe de base "lourde", vers laquelle je me verrais moi-même penché dans un tel cas.
samis
1
@samis: vous utilisez des classes nommées "A, B, C" et "Base" avec un seul champ et aucune méthode dans le code de production? Je remets cela en question.
Doc Brown