2009-04-23 6 views
6

Cela peut être une question débutante, mais existe-t-il un moyen standard de refactoriser la duplication de la propriété Wheel dans la classe abstraite tout en maintenant le cast explicite au type Part. Supposons que nous devions empêcher un FastCarWheel d'être placé sur un SlowCar, et qu'il existe de nombreuses propriétés comme celle-ci.Question de base sur le refactoring dans une classe abstraite

abstract class Car {} 

class FastCar : Car 
{ 
    public FastCarWheel Wheel { get; set; } 
} 

class SlowCar : Car 
{ 
    public SlowCarWheel Wheel { get; set; } 
} 

abstract class WheelPart {} 

class FastCarWheel: WheelPart {} 

class SlowCarWheel: WheelPart {} 

Dans ce type de scénario, est-il courant de permettre simplement ce type de duplication? Je pensais utiliser des génériques, mais il me semble que je suis en train de déplacer le problème, et cela empire pour chaque propriété supplémentaire qui se comporte ainsi.

abstract class Car <P> 
    where P : Part 
{ 
    protected abstract P Wheel { get; set; } 
} 

Merci

Répondre

0

Définir une interface de roue (IWheel):

public interface IWheel 
{ 
} 

implémentent l'interface pour FastCarWheel et SlowCarWheel par exemple

public class FastCarWheel : IWheel 
{ 
} 

Maintenant, votre classe abstraite devient:

abstract class Car 
{ 
public IWheel Wheel { get; set; } 
} 

Sous-classes de voitures sont alors libres d'utiliser la mise en œuvre de la roue de leur choix:

FastCar fastCar = new FastCar(); 
fastCar.Wheel = new FastCarWheel(); 
+0

C'est le problème, cependant. Il ne veut pas qu'une voiture ait un type de roue. Il veut FastCars pour avoir seulement FastWheels, et SlowCars pour avoir seulement SlowWheels. Une interface ne permettrait pas cela. – Joseph

+0

Si un FastCar ne peut pas avoir de SlowCarWheel, alors cette logique appartient à FastCar, et non à la classe de base. –

+0

Je suis d'accord, mais le problème est que votre code favoriserait l'utilisation de ceci (fastCar.Wheel = new SlowCarWheel();) qu'il a explicitement dit ne pas vouloir autoriser. Je suis d'accord, cependant, que cette logique n'appartient pas à la classe de base. – Joseph

1

Je pense que l'aide d'un Fast ou Slowpolicy peut aider à mettre la roue correcte pour un type de véhicule donné (où les deux Car et Wheel dépendent de la politique et un objet Car a, disons, une agrégation privée de roues).

1

Cette solution est polymorphique, mais peut-être votre seule option si vous avez besoin de visibilité au niveau de la classe de base:

abstract class Car 
{ 
    private CarWheel wheel; 
    public CarWheel Wheel 
    { 
     get { return wheel; } 
     protected set { wheel = value; } 
    } 
} 

class FastCar : Car 
{ 
    public new FastCarWheel Wheel 
    { 
     get { return base.Wheel as FastCarWheel; } 
     set { base.Wheel = value; } 
    } 
} 

class SlowCar : Car 
{ 
    public new SlowCarWheel Wheel 
    { 
     get { return base.Wheel as SlowCarWheel ; } 
     set { base.Wheel = value; } 
    } 
} 

Vous pouvez évaluer si votre classe de base fait trop. Il pourrait être possible de résoudre votre problème en divisant vos classes en plusieurs classes plus petites. D'un autre côté, parfois c'est inévitable.

1

Je voudrais créer un ICar puis définissez votre voiture de cette façon, au lieu d'une classe abstraite

interface ICar 
{ 
    IWheel Wheel {get; set;} 
} 

class FastCar: ICar 
{ 
    FastWheel fastWheel; 
    IWheel Wheel 
    { 
     get { return fastWheel; } 
     set 
     { 
      if (value is FastWheel) fastWheel = (FastWheel)value; 
     }  
    }   
} 

class SlowCar: ICar 
{ 
    SlowWheel slowWheel; 
    IWheel Wheel 
    { 
     get { return slowWheel; } 
     set 
     { 
      if (value is SlowWheel) slowWheel = (SlowWheel)value; 
     }  
    } 
} 

class FastWheel: IWheel {} 
class SlowWheel: IWheel {} 
1

Depuis votre objectif semble permettre le code client pour obtenir la propriété de retour comme WheelPart, mais seulement Définir comme une sous-classe spécifique, vous avez quelques options. Bien que j'ai peur qu'aucun d'eux ne soit très propre.

Tout d'abord, vous pouvez lancer une erreur d'exécution si le mauvais type est défini:

public abstract class Car 
    { 
     public abstract WheelPart Wheel { get; set; } 
    } 

    public class FastCar : Car 
    { 
     private FastWheel _wheel; 
     public override WheelPart Wheel 
     { 
      get { return _wheel; } 
      set 
      { 
       if (!(value is FastWheel)) 
       { 
        throw new ArgumentException("Supplied wheel must be Fast"); 
       } 
       _wheel = (FastWheel)value; 
      } 
     } 
    } 

Mais je ne voudrais pas le faire car il est très clair pour le code client que tout autre type de roue lancera une exception, et ils ne recevront aucun retour de compilateur.

Sinon, vous pouvez séparer les Getter et Setter pour la propriété de telle sorte que le type requis est très clair:

public abstract class Car 
    { 
     public abstract WheelPart Wheel { get; } 
    } 

    public class FastCar : Car 
    { 
     private FastWheel _wheel; 
     public override WheelPart Wheel 
     { 
      get { return _wheel; } 
     } 

     public void SetWheel(FastWheel wheel) 
     { 
      _wheel = wheel; 
     } 
    } 

Ceci est beaucoup plus claire au client et à mon avis une solution plus agréable si vous devez absolument exposer la getter comme classe de base WheelPart.

+0

La deuxième solution semble plutôt sympa. – Greg