2008-11-10 3 views
7

Я собираюсь попытаться задать свой вопрос в контексте простого примера ...Как использовать композицию с наследованием?

Предположим, у меня есть абстрактный автомобиль базового класса. У автомобиля есть основной объект двигателя. У меня есть метод StartEngine() в абстрактном классе Car, который делегирует запуск движка объекту Engine.

Как разрешить подклассы автомобиля (например, Ferrari) объявить объект Engine определенным двигателем (например, TurboEngine)? Нужен ли мне еще один класс Car (TurboCar)?

Я наследую простой старый объект Engine, и я не могу повторно объявить (или переопределить) его как TurboEngine в моих подклассах Car.

EDIT: Я понимаю, что могу подключить любой подкласс Engine к ссылке myEngine в моем классе Ferrari ... но как я могу вызвать методы, которые предоставляет только TurboEngine? Поскольку myEngine унаследован как базовый движок, он не включен ни в один из компонентов turbo.

Спасибо!

+0

Ooooh! Замечательно! Никогда не знал, что в SO есть тег car-analogy *! – 2011-11-17 20:22:17

ответ

9

Образец заводской картины именно для этой проблемы. Google GoF Abstract Factory {ваш предпочитаемый язык}

В дальнейшем обратите внимание, как вы можете использовать конкретные заводы для создания «полных» объектов (enzo, civic), или вы можете использовать их для создания «семейств» связанных объектов (CarbonFrame + TurboEngine, WeakFrame + WeakEngine). В конечном счете, вы всегда получаете объект Car, который реагирует на ускорение() с характерным поведением.


 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

Существует множество способов сделать это.

я бы пользу имея метод setEngine() на Car, затем с Ferrari вызовом конструктора setEngine() и передать в экземпляре TurboEngine.

0

не разоблачить внутренностей вашего класса в интерфейсе - другими словами, общественный метод Автомобиль должен быть Start, не StartEngine

если вы хотите ввести внутреннюю структуру (например, иметь только 1 двигатель), то вам нужен еще один абстрактный/базовый класс Engine, который может быть специализированным.

, то вы можете построить спортивный автомобиль из частей установки элемента m_engine к спортивному подклассу Engine, и др

EDIT: обратите внимание, что в реальном мире, турбокомпрессор не является частью двигателя, это дополнение к движку с собственным интерфейсом управления ... Но если вы хотите включить такие вещи, как это в своем движке феррари, все в порядке, просто повысился в подклассе SportsCar, чтобы сделать ваш базовый движок в TurboEngine

, но было бы лучше моделировать, чтобы компоненты были раздельными - таким образом, вы можете модернизировать свой турбокомпрессор (например, с двойным входом или с одним входом) без замены всего двигателя!

+0

@ vg1890: см. Правки; турбокомпрессор не является частью двигателя, это еще один компонент, который работает с двигателем – 2008-11-10 21:35:30

1

В зависимости от вашей конкретной семантики языка существует несколько способов сделать это.Off манжеты моя первоначальная мысль была бы обеспечить защищенный конструктор:

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

Там нет необходимости указывать подкласс автомобиля, чтобы иметь TurboEngine, пока TurboEngine является подклассом Engine. Вы можете просто указать экземпляр TurboEngine как «Двигатель для вашего Ferrari». Вы даже можете поставить DieselEngine в свой Ferrari. Они все просто Двигатели.

Автомобиль имеет двигатель. TurboEngine - это двигатель. Автомобиль может иметь TurboEngine или DieselEngine или FlintstonesEngine. Они все Двигатели.

Если вы хотите ограничить тип двигателя в вашем подклассе Car (no LawnMowerEngine в SportsCar), вы можете оставить его объявленным как Двигатель и ограничить его в методах setter.

Автомобиль имеет отношение Двигатель не ограничивает применимые подклассы двигателя.

2

Вы всегда можете использовать реферат, который защищен. Публичный «Пуск» вызывается защищенным (это будет ovveride в абстрактном классе). Таким образом, вызывающий абонент видит только Start(), а не 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 способ использовать:

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

Я думаю, что это будет работать.

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

Есть ли у вас дженерики на вашем языке? В Java я мог бы это сделать:

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

Я уверен, что есть что-то подобное в C#. Затем вы можете рассматривать экземпляр Ferrari как экземпляр подкласса Ferrari (с возвратом getEngine TurboEngine) или в качестве экземпляра суперкласса Car (когда getEngine вернет движок).

1

Вы можете использовать генераторы C#, чтобы получить то, что ищете.

Различие использования дженериков является то, что ваш Ferrari «знает», что его Engine есть-TurboEngine, в то время как Car класс не должен знать ничего нового — только что EngineType это-ан 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

Как я понимаю ваше (обновлено) вопрос, вы будете иметь, чтобы бросить двигатель автомобиля к TurboEngine типа, если вы хотите вызвать TurboEngine его методы. Это приводит к большому количеству проверок, чтобы убедиться, что у вашего автомобиля есть TurboEngine, прежде чем вы вызовете эти методы, но это то, что вы получаете. Не зная, что на самом деле стоит эта машина, я не могу думать о какой-либо причине, по которой вы не могли бы заставить двигатель и турбо-двигатель использовать один и тот же интерфейс - есть ли действительно новые методы, поддерживаемые турбонаддувом, или это просто делает то же самое по-другому - но я предполагаю, что эта метафора рано или поздно развалится.

Смежные вопросы