2009-04-23 18 views
6

Puede ser una pregunta para principiantes, pero ¿existe una forma estándar de refactorizar la duplicación de la propiedad Wheel en la clase abstracta y aún mantener la conversión explícita en el tipo Part? Supongamos que tenemos que evitar que un FastCarWheel sea puesto en un SlowCar, y que hay muchas propiedades como esta.Pregunta básica sobre la refactorización en una clase abstracta

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 {} 

En este tipo de escenario, ¿es común permitir este tipo de duplicación? Estaba pensando en hacer uso de genéricos, pero parece que estoy moviendo el tema, y ​​empeora por cada propiedad adicional que se comporta de esta manera.

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

Gracias

Respuesta

0

definir una interfaz de rueda (iWheel):

public interface IWheel 
{ 
} 

implementar la interfaz de FastCarWheel y SlowCarWheel por ejemplo

public class FastCarWheel : IWheel 
{ 
} 

Ahora su clase abstracta se convierte en:

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

Las subclases de coches son entonces libre de usar cualquier aplicación de la rueda que elijan:

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

Ese es el problema, sin embargo. NO QUIERE que ningún auto tenga ningún tipo de rueda. Quiere que FastCars solo tenga FastWheels y SlowCars para que solo tenga SlowWheels. Una interfaz no lograría esto. – Joseph

+0

Si un FastCar no puede tener un SlowCarWheel, esa lógica pertenece a FastCar, y no a la clase base. –

+0

Estoy de acuerdo, pero el problema es que su código promovería el uso de esto (fastCar.Wheel = new SlowCarWheel();) que explícitamente dijo que no quería permitir. Sin embargo, estoy de acuerdo en que esta lógica no pertenece a la clase base. – Joseph

1

Creo que el uso de un Fast o Slowpolicy puede ayudar a poner la rueda correcta para un tipo de coche dado (donde tanto Car y Wheel dependen de la política y un objeto Car tiene, por ejemplo, una agregación privada de ruedas).

1

Esta solución no es polimórfico, pero podría ser su única opción si necesita visibilidad a nivel de clase 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; } 
    } 
} 

Es posible que desee evaluar si su clase base está haciendo demasiado. Podría ser posible resolver su problema dividiendo sus clases en muchas clases más pequeñas. Por otro lado, a veces es inevitable.

1

me gustaría crear un ICAR y luego definir su Coches de esa manera, en lugar de una clase abstracta

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

Dado que su objetivo parece estar permitiendo que el código de cliente para obtener la propiedad de nuevo como un WheelPart, pero sólo establecerlo como una subclase específica, tiene un par de opciones. Aunque me temo que ninguno de los dos está muy limpio.

primer lugar que podría lanzar un error de ejecución si se establece el tipo incorrecto:

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; 
      } 
     } 
    } 

pero no me gustaría hacer esto ya que es muy claro en el código de cliente que cualquier otro tipo de rueda arrojará una excepción, y no obtendrán ningún comentario del compilador.

lo contrario, podría separar el getter y setter para la propiedad por lo que el tipo requerido es muy clara:

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; 
     } 
    } 

Esto es mucho más claro para el cliente y en mi humilde opinión una solución mejor si es absolutamente necesario exponer la getter como la clase base de WheelPart.

+0

La segunda solución parece bastante agradable. – Greg