Question

Je vais essayer de poser ma question dans le cadre d'un exemple simple ...

Disons que j'ai une voiture de classe de base abstraite. Car a un objet moteur de base. J'ai une méthode StartEngine () dans la classe abstraite Car qui délègue le démarrage du moteur à l'objet Engine.

Comment autoriser les sous-classes de Car (telles que Ferrari) à déclarer l'objet Engine comme type de moteur spécifique (par exemple, TurboEngine)? Ai-je besoin d'une autre classe de voiture (TurboCar)?

J'hérite d'un ancien objet Engine et je ne peux pas le déclarer (ou le remplacer) en tant que TurboEngine dans mes sous-classes Car.

EDIT: Je comprends que je peux connecter n'importe quelle sous-classe de Engine à la référence myEngine dans ma classe Ferrari ... mais comment puis-je appeler des méthodes que seul TurboEngine expose? MyEngine étant hérité en tant que moteur de base, aucun élément turbo n'est inclus.

Merci!

Était-ce utile?

La solution

Le motif de la fabrique abstraite est précisément pour ce problème. Google GoF Abstract Factory {votre langue préférée}

Dans ce qui suit, notez comment vous pouvez utiliser les usines de fabrication de béton pour produire des "complets". objets (enzo, civic) ou vous pouvez les utiliser pour produire des "familles". d'objets associés (CarbonFrame + TurboEngine, WeakFrame + WeakEngine). En fin de compte, vous vous retrouvez toujours avec un objet Car qui répond à accelerate () avec un comportement spécifique au type.

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

Autres conseils

Il n'est pas nécessaire de spécifier une sous-classe de voiture pour avoir un TurboEngine tant que TurboEngine est une sous-classe de Engine. Vous pouvez simplement spécifier une instance de TurboEngine en tant que moteur de votre Ferrari. Vous pouvez même mettre un moteur diesel dans votre Ferrari. Ils ne sont que des moteurs.

Une voiture a un moteur. Un TurboEngine est un moteur. Une voiture peut avoir un moteur TurboEngine, DieselEngine ou Flintstones. Ils sont tous des moteurs.

Si vous souhaitez limiter le type de moteur dans votre sous-classe de voiture (pas LawnMowerEngine dans une SportsCar), vous pouvez le laisser déclaré en tant que moteur et le limiter dans les méthodes de définition.

La relation entre la voiture et le moteur ne limite pas les sous-classes applicables de ce moteur.

Vous pouvez toujours utiliser un résumé protégé. Le public " Démarrer " appellera le protégé (qui sera sur dans la classe abstraite). Ainsi, l’appelant ne verra que Start () et non pas 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 façon de l'utiliser:

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

Je pense que cela fonctionnerait.

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

Vous pouvez utiliser les génériques C # pour obtenir ce que vous recherchez, ici.

L'utilisation de génériques est caractérisée par le fait que votre Ferrari "sait". que son moteur est un TurboEngine , alors que la classe Car ne doit rien savoir de nouveau & # 8212; seulement ce EngineType est un moteur .

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!");
    }
}

Si j'ai bien compris votre question (mise à jour), vous allez devoir lancer le moteur de la voiture sur le type TurboEngine si vous souhaitez appeler les méthodes TurboEngine . . Cela entraîne de nombreuses vérifications pour savoir si votre voiture possède un TurboEngine avant d'appeler ces méthodes, mais c'est ce que vous obtenez. Ne sachant pas ce que cette voiture représente réellement, je ne vois aucune raison pour laquelle le moteur et le moteur turbo ne partagent pas la même interface - existe-t-il de nouvelles méthodes que le turbo prend en charge, ou le fait-il tout simplement? les mêmes choses différemment - mais je suppose que cette métaphore allait s'effondrer tôt ou tard.

Avez-vous des génériques dans votre langue? En Java, je pourrais faire ceci:

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

Je suis sûr qu'il y a quelque chose de similaire en C #. Vous pouvez ensuite traiter une instance de Ferrari soit comme instance de la sous-classe Ferrari (avec getEngine renvoyant le TurboEngine), soit comme instance de la superclasse Car (lorsque getEngine retournera un moteur).

En fonction de la sémantique de votre langue, il existe plusieurs façons de procéder. Au départ, je pensais commencer par fournir un constructeur protégé:

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

n'exposez pas les éléments internes de votre classe dans l'interface. En d'autres termes, la méthode publique de Car devrait être Start, pas StartEngine

si vous souhaitez imposer une structure interne (c'est-à-dire n'avoir qu'un moteur), vous avez besoin d'un autre moteur de classe abstract / base pouvant être spécialisé.

alors vous pouvez construire une voiture de sport à partir de pièces en définissant le membre m_engine dans une sous-classe sportive de Engine, etc.

EDIT: notez que dans le monde réel, un turbocompresseur ne fait pas partie du moteur, il s’ajoute au moteur, avec sa propre interface de contrôle ... Mais si vous souhaitez inclure de tels éléments dans votre moteur ferrari, ce n’est pas un problème, il suffit de passer au niveau supérieur dans la sous-classe SportsCar pour transformer votre moteur de base en moteur TurboEngine

mais il serait préférable de modéliser les composants pour les séparer. Ainsi, vous pourrez mettre à niveau votre turbocompresseur (prise double ou simple prise, par exemple) sans remplacer le moteur complet!

Il existe de nombreuses façons de le faire.

Je serais favorable à une méthode setEngine () sur Car , puis à l'appel de constructeur Ferrari setEngine () et transmettez une instance d'un TurboEngine .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top