如何将组合与继承结合使用?

发布于 2024-07-08 20:36:21 字数 484 浏览 8 评论 0原文

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

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

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

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

编辑:我知道我可以将 Engine 的任何子类插入到我的 Ferrari 类中的 myEngine 引用中...但是我如何调用只有 TurboEngine 公开的方法? 因为 myEngine 是作为基础 Engine 继承的,所以不包含任何 Turbo 的东西。

谢谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

如此安好 2024-07-15 20:36:21

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

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


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

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();
        }
    }
︶葆Ⅱㄣ 2024-07-15 20:36:21

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

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

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

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

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.

我不会写诗 2024-07-15 20:36:21

您始终可以使用受保护的摘要。 公共“Start”将调用受保护的(将在抽象类中被 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();

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();
故人爱我别走 2024-07-15 20:36:21

我认为这会起作用。

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

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();
人间☆小暴躁 2024-07-15 20:36:21

您可以在此处使用 C# 泛型来获取您要查找的内容。

使用泛型的区别在于,您的 Ferrari “知道”其 EngineTurboEngine,而 Car > 类不必知道任何新内容,只需知道 EngineType 是一个 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!");
    }
}

You can use C# generics to get what you're looking for, here.

The distinction of using generics is that your Ferrari "knows" that its Engine is-a TurboEngine, while the Car class doesn't have to know anything new—only that EngineType is-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!");
    }
}
爱她像谁 2024-07-15 20:36:21

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

As I understand your (updated) question, you're going to have to cast the car's engine to the TurboEngine type if you want to call TurboEngine methods on it. That results in a lot of checking to see if the car you have has a TurboEngine before you call those methods, but that's what you get. Not knowing what this car is actually standing in for, I can't think of any reason you couldn't have the engine and the turbo engine share the same interface - are there really new methods that the turbo supports, or does it just do the same things differently - but I guess this metaphor was going to fall apart sooner or later.

ι不睡觉的鱼゛ 2024-07-15 20:36:21

你的语言有泛型吗? 在 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)。

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).

魔法唧唧 2024-07-15 20:36:21

根据您的特定语言语义,有几种方法可以做到这一点。 我最初的想法是提供一个受保护的构造函数:

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

Depending on your particular language semantics, there are a few ways to do this. Off the cuff my initial thought would be to provide a protected constructor:

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());
    }
}
安人多梦 2024-07-15 20:36:21

不要在接口中公开类的内部结构 - 换句话说,

如果您想强加一个内部结构(即只有 1 个引擎),那么 Car 的公共方法应该是 Start,而不是 StartEngine,那么您需要另一个抽象/可以专门化的基类引擎。

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

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

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

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!

浴红衣 2024-07-15 20:36:21

有很多方法可以做到。

我希望在 Car 上使用 setEngine() 方法,然后让 Ferrari 构造函数调用 setEngine() 并传入 TurboEngine 的实例。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文