7

簡単な例のコンテキストで質問をしようと思います...

抽象基本クラス Car があるとします。車には基本的なエンジン オブジェクトがあります。エンジンの始動を Engine オブジェクトに委譲する抽象 Car クラスに StartEngine() メソッドがあります。

Car のサブクラス (Ferrari など) が Engine オブジェクトを特定のタイプのエンジン (TurboEngine など) として宣言できるようにするにはどうすればよいですか? 別の Car クラス (TurboCar) が必要ですか?

単純な古い Engine オブジェクトを継承していますが、Car サブクラスでそれを TurboEngine として再宣言 (またはオーバーライド) することはできません。

編集: Engine の任意のサブクラスを Ferrari クラス内の myEngine 参照にプラグインできることは理解していますが、TurboEngine のみが公開するメソッドを呼び出すにはどうすればよいですか? myEngine はベース エンジンとして継承されるため、ターボ関連のものは含まれません。

ありがとう!

4

10 に答える 10

9

Abstract Factory パターンはまさにこの問題に対応しています。Google GoF Abstract Factory {ご希望の言語}

以下では、具体的なファクトリを使用して「完全な」オブジェクト (enzo、civic) を生成する方法、またはそれらを使用して関連オブジェクト (CarbonFrame + TurboEngine、WeakFrame + WeakEngine) の「ファミリ」を生成する方法に注意してください。最終的には、常にタイプ固有の動作でaccelerated()に応答する Car オブジェクトになります。


     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 のサブクラスである限り、TurboEngine を持つ Car のサブクラスを指定する必要はありません。フェラーリのエンジンとして TurboEngine のインスタンスを指定するだけです。フェラーリにディーゼルエンジンを搭載することもできます。それらはすべて単なるエンジンです。

車にはエンジンがあります。TurboEngine はエンジンです。Car は TurboEngine または DieselEngine または FlintstonesEngine を持つことができます。それらはすべてエンジンです。

Car サブクラスで Engine のタイプを制限したい (SportsCar では LawnMowerEngine ではない) 場合は、Engine として宣言したままにして、setter メソッドで制限することができます。

Car には Engine 関係があり、Engine の適用可能なサブクラスを制限しません。

于 2008-11-10T21:16:07.073 に答える
2

保護された要約はいつでも使用できます。public の "Start" は protected を呼び出します (抽象クラスでは 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

あなたの言語にはジェネリックがありますか?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# ジェネリックを使用して、探しているものを取得できます。

ジェネリックを使用することの違いは、あなたが is-aでFerrariあることを「知っている」のに対し、クラスは新しいことを何も知る必要がなく、is-anであることだけを知っていることです。EngineTurboEngineCarEngineTypeEngine

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 に答える
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 に答える
0

それを行う方法はたくさんあります。

setEngine()メソッドを onにしCarてから、Ferrariコンストラクターを呼び出しsetEngine()て a のインスタンスを渡すことをお勧めしますTurboEngine

于 2008-11-10T21:06:32.850 に答える
0

インターフェイスでクラスの内部を公開しないでください。つまり、Car のパブリック メソッドは、StartEngine ではなく、Start にする必要があります。

内部構造を強制したい場合 (つまり、エンジンを 1 つだけ持つなど)、特殊化できる別の抽象/基本クラス エンジンが必要です。

次に、m_engine メンバーを Engine のスポーティーなサブクラスに設定することにより、部品からスポーツカーを構築できます。

編集:現実の世界では、ターボチャージャーはエンジンの一部ではなく、独自の制御インターフェースを備えたエンジンへのアドオンであることに注意してください...しかし、フェラーリエンジンにこのようなものを含めたい場合は、大丈夫です。SportsCar サブクラスでアップキャストして、ベース エンジンを TurboEngine にします。

しかし、コンポーネントを別々に保つほうが良いモデリングです。そうすれば、エンジン全体を交換することなく、ターボチャージャーをアップグレードできます (たとえば、デュアル インテークとシングル インテーク)。

于 2008-11-10T21:07:11.070 に答える