Pregunta

Voy a tratar de hacer mi pregunta en el contexto de un ejemplo simple ...

Digamos que tengo una clase base abstracta Car. El coche tiene un objeto básico del motor. Tengo un método StartEngine () en la clase abstracta Car que delega el arranque del motor al objeto Engine.

¿Cómo permito que las subclases de Car (como Ferrari) declaren el objeto Engine como un tipo específico de motor (por ejemplo, TurboEngine)? ¿Necesito otra clase de automóvil (TurboCar)?

Estoy heredando un viejo objeto Engine simple y no puedo volver a declararlo (o anularlo) como TurboEngine en mis subclases de Car.

EDITAR: entiendo que puedo conectar cualquier subclase de Motor en mi referencia de Motor dentro de mi clase Ferrari ... pero ¿cómo puedo llamar a métodos que solo expone el TurboEngine? Debido a que myEngine se hereda como un motor base, no se incluye ninguna de las cosas turbo.

¡Gracias!

¿Fue útil?

Solución

El patrón Abstract Factory es precisamente para este problema. Google GoF Abstract Factory {su idioma preferido}

A continuación, observe cómo puede usar las fábricas de concreto para producir " completar " objetos (enzo, civic) o puede usarlos para producir '' familias '' de objetos relacionados (CarbonFrame + TurboEngine, WeakFrame + WeakEngine). En última instancia, siempre terminas con un objeto Car que responde a accelerate () con un comportamiento específico de tipo.


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

Otros consejos

No es necesario especificar una subclase de Car para tener un TurboEngine siempre que TurboEngine sea una subclase de Engine. Simplemente puede especificar una instancia de TurboEngine como el motor para su Ferrari. Incluso podrías poner un DieselEngine en tu Ferrari. Todos son solo motores.

Un auto tiene un motor. Un TurboEngine es un motor. Un automóvil puede tener un TurboEngine o un DieselEngine o un FlintstonesEngine. Todos son motores.

Si desea limitar el tipo de motor en su subclase de automóvil (no LawnMowerEngine en un SportsCar), puede dejarlo declarado como motor y limitarlo en los métodos de establecimiento.

El automóvil tiene una relación de motor no limita las subclases de motor aplicables.

Siempre puede usar un resumen que esté protegido. El público " Inicio " llamará a la protegida (que estará en la clase abstracta). De esta forma, la persona que llama solo ve Start () y no 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!!!");
    }

}

-La forma de usarlo:

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

Creo que esto funcionarí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();

Puede usar genéricos de C # para obtener lo que está buscando, aquí.

La distinción del uso de genéricos es que su Ferrari " sabe " que su Engine es un TurboEngine , mientras que la clase Car no tiene que saber nada nuevo & # 8212; solo ese EngineType es-un 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!");
    }
}

Según entiendo su pregunta (actualizada), tendrá que proyectar el motor del automóvil al tipo TurboEngine si desea llamar a los métodos TurboEngine . . Eso da como resultado muchas comprobaciones para ver si el automóvil que tiene tiene un TurboEngine antes de llamar a esos métodos, pero eso es lo que obtiene. Sin saber qué representa realmente este automóvil, no puedo pensar en ninguna razón por la que no pueda tener el motor y el motor turbo compartiendo la misma interfaz: ¿hay métodos realmente nuevos que el turbo admite, o simplemente lo hace? las mismas cosas de manera diferente, pero supongo que esta metáfora se desmoronaría tarde o temprano.

¿Tiene genéricos en su idioma? En Java podría hacer esto:

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

Estoy seguro de que hay algo similar en C #. Luego puede tratar una instancia de Ferrari como una instancia de la subclase Ferrari (con getEngine devolviendo el TurboEngine) o como una instancia de la superclase de automóviles (cuando getEngine devolverá un motor).

Dependiendo de la semántica de su idioma en particular, hay algunas formas de hacerlo. De improviso, mi pensamiento inicial sería proporcionar un constructor protegido:

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

no exponga los elementos internos de su clase en la interfaz; en otras palabras, el método público de Car debería ser Start, no StartEngine

si desea imponer una estructura interna (es decir, como tener solo 1 motor), entonces necesita otro motor de clase abstracta / base que pueda especializarse.

entonces puedes construir un auto deportivo con partes configurando el miembro m_engine en una subclase deportiva de Engine, et al

EDITAR: tenga en cuenta que en el mundo real, un turbocompresor no es parte del motor, es un complemento del motor, con su propia interfaz de control ... Pero si desea incluir cosas como esta en su motor Ferrari, está bien, solo en la subclase SportsCar para convertir su motor base en un TurboEngine

pero sería mejor modelar mantener los componentes separados, de esa manera puede actualizar su turbocompresor (admisión doble versus admisión única, por ejemplo) ¡sin reemplazar todo el motor!

Hay muchas formas de hacerlo.

Yo preferiría tener un método setEngine () en Car , y luego hacer que el constructor Ferrari llame a setEngine () y pase una instancia de un TurboEngine .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top