2009-04-23 12 views
6

Dies mag eine Anfängerfrage sein, aber gibt es eine Standardmethode, um die Duplizierung der Wheel -Eigenschaft in die abstrakte Klasse umzuwandeln und trotzdem die explizite Umwandlung in den Part-Typ beizubehalten. Nehmen wir an, wir müssen verhindern, dass ein FastCarWheel auf eine SlowCar gesetzt wird und dass es viele Eigenschaften wie diese gibt.Grundlegende Frage zum Refactoring in eine abstrakte Klasse

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

In dieser Art von Szenario ist es üblich, nur diese Art der Vervielfältigung zu erlauben? Ich dachte daran, Generics zu verwenden, aber es scheint, als würde ich das Problem verschieben, und es wird schlimmer für jede weitere Eigenschaft, die sich so verhält.

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

Dank

Antwort

0

Definieren Sie eine Rad-Schnittstelle (iWheel):

public interface IWheel 
{ 
} 

Implementieren Sie die Schnittstelle für FastCarWheel und SlowCarWheel zB

public class FastCarWheel : IWheel 
{ 
} 

Jetzt ist Ihre abstrakte Klasse wird:

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

Subklassen von Auto sind dann frei zu verwenden, was die Umsetzung des Rades sie wählen:

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

Das ist das Problem, obwohl. Er möchte kein Auto irgendein Rad haben. Er möchte, dass FastCars nur FastWheels und SlowCars nur SlowWheels haben. Eine Schnittstelle würde dies nicht erreichen. – Joseph

+0

Wenn ein FastCar kein SlowCarWheel haben kann, gehört diese Logik zu FastCar und nicht zur Basisklasse. –

+0

Ich stimme zu, aber das Problem ist, dass Ihr Code die Verwendung dieser (FastCar.Wheel = new SlowCarWheel();) fördern würde, die er ausdrücklich sagte, dass er nicht zulassen wollte. Ich stimme jedoch zu, dass diese Logik nicht in die Basisklasse gehört. – Joseph

1

denke ich, mit ein Fast oder Slowpolicy helfen, das richtige Rad für einen bestimmten Fahrzeugtyp setzen kann (wo beide Car und Wheel sind abhängig von der Richtlinie und ein Car Objekt hat, sagen wir, eine private Aggregation von Rädern).

1

Diese Lösung ist nicht polymorph, sondern könnte Ihre einzige Option sein, wenn Sie die Sichtbarkeit auf der Basisklasse Ebene benötigen:

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

Vielleicht möchten Sie, wenn Ihre Basisklasse zu viel tut, bewerten. Es könnte möglich sein, Ihr Problem zu lösen, indem Sie Ihre Klassen in viele kleinere Klassen aufteilen. Auf der anderen Seite ist es manchmal unvermeidlich.

1

Ich würde eine ICAR erstellen und dann definieren Sie Ihre Autos auf diese Weise, statt einer abstrakten Klasse

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

Da Ihr Ziel, den Client ermöglicht Code zu sein scheint die Eigenschaft zurück als WheelPart zu bekommen, aber nur Wenn Sie eine bestimmte Unterklasse festlegen, haben Sie mehrere Möglichkeiten. Obwohl ich befürchte, dass keiner von ihnen sehr sauber ist.

Zum einen könnten Sie einen Laufzeitfehler auslösen, wenn der falsche Typ festgelegt ist:

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

Aber ich würde das nicht tun, da es sehr unklar, an den Client-Code ist, dass jede andere Art von Rad ein werfen Ausnahme, und sie erhalten kein Compiler-Feedback.

Andernfalls könnten Sie die Getter und Setter für die Eigenschaft trennen, so dass die erforderliche Art ist sehr klar:

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

Dies ist viel klarer an den Client und IMHO eine schönere Lösung, wenn Sie unbedingt die aussetzen müssen Getter als Basis WheelPart-Klasse.

+0

Die zweite Lösung scheint ziemlich nett zu sein. – Greg