Search code examples
oopinheritancecompositionooadcar-analogy

How do I use composition with inheritance?


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 StartEngine() in the abstract Car class that delegates the starting of the engine to the Engine object.

How do I allow subclasses of Car (like Ferrari) to declare the Engine object as a specific type of engine (e.g., TurboEngine)? Do I need another Car class (TurboCar)?

I'm inheriting a plain old Engine object and I cannot re-declare (or override) it as a TurboEngine in my Car subclasses.

EDIT: I understand that I can plug any subclass of Engine into myEngine reference within my Ferrari class...but how can I call methods that only the TurboEngine exposes? Because myEngine is inherited as a base Engine, none of the turbo stuff is included.

Thanks!


Solution

  • The Abstract Factory pattern is precisely for this problem. Google GoF Abstract Factory {your preferred language}

    In the following, note how you can either use the concrete factories to produce "complete" objects (enzo, civic) or you can use them to produce "families" of related objects (CarbonFrame + TurboEngine, WeakFrame + WeakEngine). Ultimately, you always end up with a Car object that responds to accelerate() with type-specific behavior.


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