2008-11-10 11 views
7

Spróbuję zadać moje pytanie w kontekście prostego przykładu ...Jak używać kompozycji z dziedziczeniem?

Załóżmy, że mam abstrakcyjny samochód klasy podstawowej. Samochód ma podstawowy obiekt silnika. Mam metodę StartEngine() w abstrakcyjnej klasie samochodów, która deleguje uruchamianie silnika do obiektu Engine.

Jak zezwolić na podklasy samochodu (np. Ferrari) na zadeklarowanie obiektu Engine jako określonego typu silnika (np. TurboEngine)? Czy potrzebuję kolejnej klasy samochodów (TurboCar)?

Odziedziczę zwykły stary obiekt Engine i nie mogę ponownie zadeklarować (lub przesłonić) go jako TurboEngine w podklasach mojego samochodu.

EDIT: Rozumiem, że mogę podłączyć każdą podklasę silnika w odniesieniu myEngine w mojej klasie Ferrari ... ale w jaki sposób można wywołać metody że tylko TurboEngine naraża? Ponieważ myEngine jest dziedziczony jako silnik bazowy, żaden z elementów turbo nie jest zawarty.

Dzięki!

+0

Ooooh! Wspaniale! Nigdy nie wiedziałem, że w tagu SO istnieje tag * car-analogy *! –

Odpowiedz

9

Abstrakcyjny wzór fabryczny jest właśnie dla tego problemu. Fabryka abstraktów Google GoF {preferowany język}

W dalszej części zwróć uwagę, w jaki sposób możesz użyć fabryk do produkcji "kompletnych" obiektów (enzo, civic) lub użyć ich do stworzenia "rodzin" powiązanych obiektów (CarbonFrame + TurboEngine, WeakFrame + WeakEngine). Ostatecznie zawsze kończy się na obiekcie Car, który reaguje na accelerate() z zachowaniem specyficznym dla typu.


 using System; 


    abstract class CarFactory 
    { 
     public static CarFactory FactoryFor(string manufacturer){ 
      switch(manufacturer){ 
       case "Ferrari" : return new FerrariFactory(); 
       case "Honda" : return new HondaFactory(); 
       default: 
        throw new ArgumentException("Unknown car manufacturer. Please bailout industry."); 
      } 
     } 

     public abstract Car createCar(); 
     public abstract Engine createEngine(); 
     public abstract Frame createFrame(); 

    } 

    class FerrariFactory : CarFactory 
    { 
     public override Car createCar() 
     { 
      return new Ferrari(createEngine(), createFrame()); 
     } 

     public override Engine createEngine() 
     { 
      return new TurboEngine(); 
     } 

     public override Frame createFrame() 
     { 
      return new CarbonFrame(); 
     } 
    } 

    class HondaFactory : CarFactory 
    { 
     public override Car createCar() 
     { 
      return new Honda(createEngine(), createFrame()); 
     } 

     public override Engine createEngine() 
     { 
      return new WeakEngine(); 
     } 

     public override Frame createFrame() 
     { 
      return new WeakFrame(); 
     } 
    } 

    abstract class Car 
    { 
     private Engine engine; 
     private Frame frame; 

     public Car(Engine engine, Frame frame) 
     { 
      this.engine = engine; 
      this.frame = frame; 
     } 

     public void accelerate() 
     { 
      engine.setThrottle(1.0f); 
      frame.respondToSpeed(); 
     } 

    } 

    class Ferrari : Car 
    { 
     public Ferrari(Engine engine, Frame frame) : base(engine, frame) 
     { 
      Console.WriteLine("Setting sticker price to $250K"); 
     } 
    } 

    class Honda : Car 
    { 
     public Honda(Engine engine, Frame frame) : base(engine, frame) 
     { 
      Console.WriteLine("Setting sticker price to $25K"); 
     } 
    } 

    class KitCar : Car 
    { 
     public KitCar(String name, Engine engine, Frame frame) 
      : base(engine, frame) 
     { 
      Console.WriteLine("Going out in the garage and building myself a " + name); 
     } 
    } 

    abstract class Engine 
    { 
     public void setThrottle(float percent) 
     { 
      Console.WriteLine("Stomping on accelerator!"); 
      typeSpecificAcceleration(); 
     } 

     protected abstract void typeSpecificAcceleration(); 
    } 

    class TurboEngine : Engine 
    { 
     protected override void typeSpecificAcceleration() 
     { 
      Console.WriteLine("Activating turbo"); 
      Console.WriteLine("Making noise like Barry White gargling wasps"); 
     } 
    } 

    class WeakEngine : Engine 
    { 
     protected override void typeSpecificAcceleration() 
     { 
      Console.WriteLine("Provoking hamster to run faster"); 
      Console.WriteLine("Whining like a dentist's drill"); 
     } 
    } 

    abstract class Frame 
    { 
     public abstract void respondToSpeed(); 
    } 

    class CarbonFrame : Frame 
    { 
     public override void respondToSpeed() 
     { 
      Console.WriteLine("Activating active suspension and extending spoilers"); 
     } 
    } 

    class WeakFrame : Frame 
    { 
     public override void respondToSpeed() 
     { 
      Console.WriteLine("Loosening bolts and vibrating"); 
     } 
    } 

    class TestClass 
    { 
     public static void Main() 
     { 
      CarFactory ferrariFactory = CarFactory.FactoryFor("Ferrari"); 
      Car enzo = ferrariFactory.createCar(); 
      enzo.accelerate(); 

      Console.WriteLine("---"); 
      CarFactory hondaFactory = CarFactory.FactoryFor("Honda"); 
      Car civic = hondaFactory.createCar(); 
      civic.accelerate(); 

      Console.WriteLine("---"); 
      Frame frame = hondaFactory.createFrame(); 
      Engine engine = ferrariFactory.createEngine(); 
      Car kitCar = new KitCar("Shaker", engine, frame); 
      kitCar.accelerate(); 

      Console.WriteLine("---"); 
      Car kitCar2 = new KitCar("LooksGreatGoesSlow", hondaFactory.createEngine(), ferrariFactory.createFrame()); 
      kitCar2.accelerate(); 
     } 
    } 
0

Istnieje wiele sposobów na zrobienie tego.

ja sprzyjać obejmujący sposób setEngine() na Car, następnie po połączenia Ferrari konstruktora setEngine() i przechodzą w instancji TurboEngine.

0

nie wystawiać wnętrzności swojej klasie w interfejsie - innymi słowy, metoda publiczna Samochód powinien być start, nie StartEngine

jeśli chcą narzucić strukturę wewnętrzną (tj. jak posiadanie tylko 1 silnika), potrzebujesz kolejnego silnika klasy abstrakcyjnej/bazowej, który może być wyspecjalizowany.

wtedy można zbudować samochód sportowy z części poprzez ustawienie elementu m_engine do sportowej podklasy Engine, et al

EDIT: zauważ, że w realnym świecie, turbosprężarka nie jest częścią silnika, jest dodatkiem do silnika, z własnym interfejsem sterowania ... Ale jeśli chcesz włączyć takie rzeczy do swojego silnika Ferrari, to jest w porządku, po prostu upcast w podklasie SportsCar, aby twój bazowy silnik w TurboEngine

ale lepiej byłoby, gdyby komponenty były oddzielone od siebie - w ten sposób można zaktualizować turbosprężarkę (na przykład podwójne spożycie w stosunku do pojedynczego spożycia) bez wymiany całego silnika!

+0

@ vg1890: zobacz zmiany; turbosprężarka nie jest częścią silnika, to kolejny element współpracujący z silnikiem –

1

W zależności od wybranej semantyki języka, można to zrobić na kilka sposobów.Off mankietu moja pierwsza myśl byłoby zapewnienie zabezpieczonej konstruktora:

public class Car { 
    private Engine engine; 

    public Car() { 
     this(new Engine()); 
    } 

    protected Car(Engine engine) { 
     this.engine = engine; 
    } 

    public void start() { 
     this.engine.start(); 
    } 
} 

public class Ferrari { 
    public Ferrari() { 
     super(new TurboEngine()); 
    } 
} 
2

Nie ma potrzeby, aby określić podklasę Car mieć TurboEngine dopóki TurboEngine jest podklasą Engine. Możesz po prostu określić instancję TurboEngine jako Engine for your Ferrari. Możesz nawet umieścić DieselEngine w swoim Ferrari. Wszystkie są po prostu silnikami.

Samochód ma silnik. TurboEngine to silnik. Samochód może mieć TurboEngine lub DieselEngine lub FlintstonesEngine. Wszystkie są silnikami.

Jeśli chcesz ograniczyć typ silnika w podklasie samochodu (bez urządzenia LawnMowerEngine w SportsCar), możesz zostawić go zadeklarowany jako Engine i ograniczyć go w metodach ustawiania.

Samochód ma związek silnika nie ogranicza odpowiednich podklas silnika.

2

Zawsze można użyć abstrakcyjnego, który jest chroniony. Publiczny "Start" wywoła chroniony (który będzie Ovveride w klasie abstrakcyjnej). W ten sposób wywołujący zobaczy tylko Start(), a nie StartEngine().

abstract class Car { 
    private Engine engine; 

    public Car() { 
     this.engine = new Engine(); 
    } 

    protected Car(Engine engine) { 
     this.engine = engine; 
    } 

    public void Start() 
    { 
     this.StartEngine(); 
    } 
    protected abstract void StartEngine(); 
} 

public class Ferrari : Car 
{ 
    public Ferrari() { 

    } 
    protected override void StartEngine() 
    { 
     Console.WriteLine("TURBO ENABLE!!!"); 
    } 

} 

-The sposób z niego korzystać:

Car c = new Ferrari(); 
c.Start(); 
1

myślę, że to będzie działać.

public class Car 
{ 
    private Engine engine; 
    public virtual Engine CarEngine 
    { 
     get { return engine;} 
    } 

    public StartEngine() 
    { 
     CarEngine.Start(); 
    } 
} 

public class Engine 
{ 
    public virtual void Start() 
    { 
     Console.Writeline("Vroom"); 
    } 
} 

public class TurboEngine : Engine 
{ 
    public override void Start() 
    { 
     Console.Writeline("Vroom pSHHHHHHH"); 
    }  

    // TurboEngine Only method 
    public double BoostPressure() 
    { 
    } 
} 

public class Ferrari : Car 
{ 
    private TurboEngine engine; 
    public override Engine CarEngine 
    { 
     return engine; 
    } 
} 

Ferrari = car new Ferrari(); 
// Will call Start on TurboEngine() 
car.StartEngine(); 
// Upcast to get TurboEngine stuff 
Console.WriteLine(car.CarEngine as TurboEngine).BoostPressure(); 
1

Czy masz leki generyczne w swoim języku? W Javie mogłem to zrobić:

class Engine {} 

abstract class Car<E extends Engine> 
{ 
    private E engine; 
    public E getEngine() { return engine; } 
} 

class TurboEngine extends Engine {} 

class Ferrari extends Car<TurboEngine> 
{ 
    // Ferrari now has a method with this signature: 
    // public TurboEngine getEngine() {} 
} 

Jestem pewien, że jest coś podobnego w C#. Następnie można potraktować wystąpienie Ferrari jako instancję podklasy Ferrari (z funkcją getEngine zwracającą TurboEngine) lub jako wystąpienie superklasy samochodów (gdy getEngine zwróci silnik).

1

Możesz użyć generycznych C#, aby uzyskać to, czego szukasz, tutaj.

Rozróżnienie używania rodzajowych jest to, że Ferrari „wie”, że jego Engine is-a TurboEngine, natomiast klasa Car nie musi wiedzieć nic nowego — tylko że EngineType ma-an Engine.

class Program 
{ 
    static void Main(string[] args) 
    { 
     Ferrari ferarri = new Ferrari(); 
     ferarri.Start(); 
     ferarri.Boost(); 
    } 
} 
public class Car<EngineType> where EngineType : Engine, new() 
{ 
    protected EngineType engine; 

    public Car() 
    { 
     this.CreateEngine(); 
    } 
    protected void CreateEngine() 
    { 
     this.engine = new EngineType(); 
    } 
    public void Start() 
    { 
     engine.Start(); 
    } 
} 

public class Ferrari : Car<TurboEngine> 
{ 
    public void Boost() 
    { 
     engine.Boost(); 
    } 
} 

public class Engine 
{ 
    public virtual void Start() 
    { 
     Console.WriteLine("Vroom!"); 
    } 
} 
public class TurboEngine : Engine 
{ 
    public void Boost() 
    { 
     Console.WriteLine("Hang on to your teeth..."); 
    } 
    public override void Start() 
    { 
     Console.WriteLine("VROOOOM! VROOOOM!"); 
    } 
} 
1

Jak zrozumieć (Aktualizacja) pytanie, będziemy musieli oddać silnik samochodu do rodzaju TurboEngine jeśli chcesz zadzwonić TurboEngine metod na to. To powoduje, że przed wywołaniem tych metod, zanim otrzymasz te metody, będziesz musiał sprawdzić, czy masz samochód, ale to właśnie otrzymujesz. Nie wiedząc, na czym właściwie polega ten samochód, nie mogę wymyślić żadnego powodu, dla którego nie mógłbyś mieć silnika i silnika turbo, które dzielą ten sam interfejs - czy są naprawdę nowe metody obsługiwane przez turbosprężarkę? te same rzeczy inaczej - ale myślę, że ta metafora prędzej czy później rozpadnie się.

Powiązane problemy