How do I use composition with inheritance?

前端 未结 10 2399
星月不相逢
星月不相逢 2021-02-09 04:41

I\'m going to try to ask my question in the context of a simple example...

Let\'s say I have an abstract base class Car. Car has-a basic Engine object. I have a method

相关标签:
10条回答
  • 2021-02-09 05:10

    There's no need to specify a subclass of Car to have a TurboEngine as long as TurboEngine is a subclass of Engine. You can just specify an instance of TurboEngine as the Engine for your Ferrari. You could even put a DieselEngine in your Ferrari. They're all just Engines.

    A Car has an Engine. A TurboEngine is an Engine. A Car can have a TurboEngine or a DieselEngine or a FlintstonesEngine. They're all Engines.

    If you want to limit the type of Engine in your Car subclass (no LawnMowerEngine in a SportsCar), you can leave it declared as Engine and limit it in the setter methods.

    The Car has an Engine relationship doesn't limit the applicable subclasses of Engine.

    0 讨论(0)
  • 2021-02-09 05:10

    I think this would work.

    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();
    
    0 讨论(0)
  • 2021-02-09 05:14

    don't expose the internals of your class in the interface - in other words, the public method of Car should be Start, not StartEngine

    if you want to impose an internal structure (i.e. like having only 1 engine) then you need another abstract/base class Engine that can be specialized.

    then you can construct a sports car out of parts by setting the m_engine member to a sporty subclass of Engine, et al

    EDIT: note that in the real world, a turbocharger is not part of the engine, it is an add-on to the engine, with its own control interface... But if you want to include things like this in your ferrari engine, that's ok, just upcast in the SportsCar subclass to make your base Engine into a TurboEngine

    but it would be better modeling to keep the components separate - that way you can upgrade your turbocharger (dual intake vs single intake, for example) without replacing the entire engine!

    0 讨论(0)
  • 2021-02-09 05:17

    There are lots of ways it could be done.

    I would favour having a setEngine() method on Car, then having the Ferrari constructor call setEngine() and pass in an instance of a TurboEngine.

    0 讨论(0)
  • 2021-02-09 05:18

    You can always use an abstract that is protected. The public "Start" will call the protected (that will be ovveride in the abstract class). This way the caller only see the Start() and not the 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 way to use it:

    Car c = new Ferrari();
    c.Start();
    
    0 讨论(0)
  • 2021-02-09 05:19

    Do you have generics in your language? In Java I could do this:

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

    I'm sure there's something similar in C#. You can then treat an instance of Ferrari as either an instance of the Ferrari subclass (with getEngine returning the TurboEngine) or as an instance of the Car superclass (when getEngine will return an Engine).

    0 讨论(0)
提交回复
热议问题