如何使用带继承的合成?

时间:2008-11-10 21:02:23

标签: oop inheritance composition ooad car-analogy

我将尝试在一个简单的例子中提出我的问题......

假设我有一个抽象基类Car。 Car有一个基本的Engine对象。我在抽象Car类中有一个方法StartEngine(),它将引擎的启动委托给Engine对象。

如何允许Car的子类(如法拉利)将Engine对象声明为特定类型的引擎(例如TurboEngine)?我需要另一辆车级(TurboCar)吗?

我正在继承一个普通的旧Engine对象,我不能在我的Car子类中重新声明(或覆盖)它作为TurboEngine。

编辑:我知道我可以在我的法拉利课程中将任何Engine的子类插入myEngine参考中......但是如何调用只有TurboEngine公开的方法呢?因为myEngine是作为基础引擎继承的,所以没有包含任何turbo内容。

谢谢!

10 个答案:

答案 0 :(得分:9)

抽象工厂模式正是针对这个问题。 Google GoF抽象工厂{您的首选语言}

在下文中,请注意如何使用具体工厂生成“完整”对象(enzo,civic),或者您可以使用它们来生成相关对象的“族”(CarbonFrame + TurboEngine,WeakFrame + WeakEngine)。最终,您总是会得到一个Car对象,它响应使用类型特定行为的accele()。


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

答案 1 :(得分:2)

只要TurboEngine是Engine的子类,就不需要指定Car的子类来拥有TurboEngine。您只需将TurboEngine的实例指定为法拉利的引擎即可。你甚至可以在法拉利里放置DieselEngine。他们都只是发动机。

汽车有引​​擎。 TurboEngine是一个引擎。汽车可以配备TurboEngine或DieselEngine或FlintstonesEngine。他们都是发动机。

如果要限制Car子类中Engine的类型(SportsCar中没有LawnMowerEngine),可以将其声明为Engine并将其限制在setter方法中。

Car具有Engine关系并不限制Engine的适用子类。

答案 2 :(得分:2)

您始终可以使用受保护的摘要。公共“开始”将调用受保护的(在抽象类中将是ovveride)。这样调用者只能看到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();

答案 3 :(得分:1)

根据您的特定语言语义,有几种方法可以执行此操作。关闭袖口我最初的想法是提供受保护的构造函数:

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

答案 4 :(得分:1)

我认为这样可行。

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

答案 5 :(得分:1)

你的语言是否有泛型?在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#中有类似的东西。然后,您可以将法拉利的实例视为法拉利子类的一个实例(使用getEngine返回TurboEngine)或作为Car超类的实例(当getEngine将返回引擎时)。

答案 6 :(得分:1)

您可以使用C#generics来获取您所需的内容。

使用泛型的区别在于,Ferrari“知道”其Engine是 - TurboEngine,而Car类不必知道任何内容仅限新EngineTypeEngine

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

答案 7 :(得分:1)

据我了解您的(更新)问题,如果要在其上调用TurboEngine方法,则必须将汽车的引擎转换为TurboEngine类型。在你调用这些方法之前,这会导致大量检查,看看你的车是否有TurboEngine,但这就是你得到的。不知道这辆车实际上代表什么,我想不出你有什么理由不能让发动机和涡轮增压发动机共用相同的接口 - 涡轮机是否有新的方法支持,或者它只是做同样的事情也不同 - 但我想这个比喻迟早会崩溃。

答案 8 :(得分:0)

有很多方法可以做到。

我赞成在setEngine()上使用Car方法,然后让Ferrari构造函数调用setEngine()并传入TurboEngine的实例。< / p>

答案 9 :(得分:0)

不要在界面中暴露你的类的内部 - 换句话说,Car的公共方法应该是Start,而不是StartEngine

如果你想强加一个内部结构(比如只有1个引擎)那么你需要另一个可以专门化的抽象/基类引擎。

然后你可以通过将m_engine成员设置为Engine的运动子类来构建一辆跑车,等等

编辑:请注意,在现实世界中,涡轮增压器不是引擎的一部分,它是引擎的附加组件,具有自己的控制界面......但是如果你想在你的内容中包含这样的东西法拉利引擎,没关系,只是在SportsCar子类中向上翻转,使你的基础引擎成为TurboEngine

但是将组件分开是更好的建模方式 - 这样你可以升级你的涡轮增压器(例如双进气口和单进气口),而无需更换整个发动机!