Вопрос

Я попробую задать свой вопрос в контексте простого примера...

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

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

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

РЕДАКТИРОВАТЬ: Я понимаю, что могу подключить любой подкласс Engine к ссылке myEngine внутри моего класса Ferrari... но как я могу вызывать методы, которые предоставляет только TurboEngine?Поскольку myEngine унаследован как базовый движок, никакие турбо-функции не включены.

Спасибо!

Это было полезно?

Решение

Паттерн «Абстрактная фабрика» предназначен именно для решения этой проблемы.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();
        }
    }

Другие советы

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

У автомобиля есть двигатель.TurboEngine — это двигатель.Автомобиль может иметь турбодвигатель, дизельный двигатель или двигатель Флинстоунов.Они все Двигатели.

Если вы хотите ограничить тип двигателя в подклассе вашего автомобиля (без LawnMowerEngine в спортивном автомобиле), вы можете оставить его объявленным как Engine и ограничить его в методах установки.

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

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

}

-Способ использования:

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

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

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();

Вы можете использовать дженерики 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!");
    }
}

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

Есть ли у вас дженерики на вашем языке?В 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 возвращает Engine).

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

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

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

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

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

РЕДАКТИРОВАТЬ:обратите внимание, что в реальном мире турбокомпрессор не является частью двигателя, а является надстройкой к двигателю со своим интерфейсом управления...Но если вы хотите включить подобные вещи в свой двигатель Ferrari, это нормально, просто выполните преобразование в подклассе SportsCar, чтобы превратить базовый двигатель в TurboEngine.

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

Есть много способов это сделать.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top