7

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

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

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

我继承了一个普通的旧 Engine 对象,我无法在我的 Car 子类中将它重新声明(或覆盖)为 TurboEngine。

编辑: 我知道我可以将 Engine 的任何子类插入到我的 Ferrari 类中的 myEngine 引用中......但是我怎样才能调用只有 TurboEngine 公开的方法?因为 myEngine 是作为基础引擎继承的,所以不包括任何涡轮增压的东西。

谢谢!

4

10 回答 10

9

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

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


     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();
        }
    }
于 2008-11-10T21:07:02.630 回答
2

只要 TurboEngine 是 Engine 的子类,就不需要指定 Car 的子类来拥有 TurboEngine。您可以指定一个 TurboEngine 实例作为您的法拉利引擎。您甚至可以在您的法拉利中安装柴油发动机。他们都只是引擎。

汽车有引擎。TurboEngine 是一个引擎。汽车可以有涡轮发动机或柴油发动机或燧石发动机。他们都是引擎。

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

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

于 2008-11-10T21:16:07.073 回答
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();
于 2008-11-10T21:21:44.347 回答
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());
    }
}
于 2008-11-10T21:12:08.110 回答
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();
于 2008-11-10T21:28:08.373 回答
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# 中也有类似的东西。然后,您可以将 Ferrari 的实例视为 Ferrari 子类的实例(getEngine 返回 TurboEngine)或 Car 超类的实例(此时 getEngine 将返回 Engine)。

于 2008-11-10T22:05:57.660 回答
1

您可以在此处使用 C# 泛型来获取所需的内容。

使用泛型的区别在于你Ferrari“知道”它的Engineis-a TurboEngine,而Car类不必知道任何新的东西——只有EngineTypeis-an 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!");
    }
}
于 2008-11-10T22:07:21.633 回答
1

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

于 2008-11-10T22:10:00.803 回答
0

有很多方法可以做到。

我希望有一个setEngine()方法 on Car,然后Ferrari调用构造函数setEngine()并传入一个TurboEngine.

于 2008-11-10T21:06:32.850 回答
0

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

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

然后您可以通过将 m_engine 成员设置为 Engine 的运动子类等来用零件构造一辆跑车

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

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

于 2008-11-10T21:07:11.070 回答