سؤال

سأحاول أن أطرح سؤالي في سياق مثال بسيط ...

لنفترض أن لدي سيارة من الفئة الأساسية المجردة.السيارة لديها كائن المحرك الأساسي.لدي طريقة StartEngine() في فئة Car المجردة التي تقوم بتفويض بدء تشغيل المحرك إلى كائن المحرك.

كيف أسمح للفئات الفرعية من السيارة (مثل فيراري) بإعلان كائن المحرك كنوع معين من المحرك (على سبيل المثال، TurboEngine)؟هل أحتاج إلى فئة سيارة أخرى (TurboCar)؟

لقد ورثت كائن Engine قديمًا ولا يمكنني إعادة الإعلان عنه (أو تجاوزه) باعتباره TurboEngine في الفئات الفرعية لسيارتي.

يحرر: أدرك أنه يمكنني توصيل أي فئة فرعية من المحرك إلى مرجع myEngine ضمن فئة فيراري الخاصة بي... ولكن كيف يمكنني استدعاء الأساليب التي يكشفها TurboEngine فقط؟نظرًا لأن myEngine موروث كمحرك أساسي، لم يتم تضمين أي من عناصر التوربو.

شكرًا!

هل كانت مفيدة؟

المحلول

نمط Abstract Factory مخصص لهذه المشكلة على وجه التحديد.Google GoF Abstract Factory {لغتك المفضلة}

في ما يلي، لاحظ كيف يمكنك إما استخدام مصانع الخرسانة لإنتاج كائنات "كاملة" (enzo، civic) أو يمكنك استخدامها لإنتاج "عائلات" من الكائنات ذات الصلة (CarbonFrame + TurboEngine، WeakFrame + WeakEngine).في النهاية، ينتهي بك الأمر دائمًا إلى الحصول على كائن 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();
        }
    }

نصائح أخرى

ليست هناك حاجة لتحديد فئة فرعية من السيارة للحصول على محرك TurboEngine طالما أن TurboEngine هو فئة فرعية من المحرك.يمكنك فقط تحديد مثيل TurboEngine كمحرك لسيارتك الفيراري.يمكنك حتى وضع محرك الديزل في سيارتك الفيراري.كلهم مجرد محركات

السيارة لديها محرك.محرك TurboEngine هو محرك.يمكن أن تحتوي السيارة على محرك TurboEngine أو محرك DieselEngine أو محرك Flintstones.كلهم محركات

إذا كنت تريد تحديد نوع المحرك في الفئة الفرعية لسيارتك (لا يوجد محرك جزازة العشب في سيارة رياضية)، فيمكنك تركه مُعلنًا كمحرك وتقييده في طرق الضبط.

العلاقة بين السيارة والمحرك لا تحد من الفئات الفرعية المعمول بها للمحرك.

يمكنك دائمًا استخدام الملخص المحمي.سوف يقوم "البدء" العام باستدعاء المحمي (الذي سيتم تجاوزه في الفئة المجردة).بهذه الطريقة يرى المتصل فقط 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();

أعتقد أن هذا سينجح

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

يمكنك استخدام الأدوية العامة لـ C# للحصول على ما تبحث عنه هنا.

الفرق بين استخدام الأدوية الجنيسة هو أن الخاص بك Ferrari "يعلم" أنه Engine هو TurboEngine, ، بينما ال 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!");
    }
}

كما أفهم سؤالك (المحدث)، سيتعين عليك توجيه محرك السيارة إلى TurboEngine اكتب إذا كنت تريد الاتصال TurboEngine الأساليب عليه.وينتج عن ذلك الكثير من عمليات التحقق لمعرفة ما إذا كانت السيارة التي لديك بها TurboEngine قبل استدعاء تلك الأساليب، ولكن هذا ما تحصل عليه.لا أعرف ما الذي تمثله هذه السيارة بالفعل، ولا أستطيع التفكير في أي سبب يمنعك من مشاركة المحرك والمحرك التوربيني في نفس الواجهة - هل هناك بالفعل طرق جديدة يدعمها التوربو، أم أنه يفعل ذلك فقط نفس الأشياء بشكل مختلف - ولكن أعتقد أن هذه الاستعارة سوف تنهار عاجلاً أم آجلاً.

هل لديك أسماء عامة في لغتك؟في 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) أو كمثيل للفئة الفائقة للسيارة (عندما تقوم getEngine بإرجاع محرك).

اعتمادًا على دلالات اللغة الخاصة بك، هناك عدة طرق للقيام بذلك.فكرتي الأولية هي توفير منشئ محمي:

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

لا تكشف عن الأجزاء الداخلية لفصلك في الواجهة - بمعنى آخر، يجب أن تكون الطريقة العامة للسيارة هي Start، وليس StartEngine

إذا كنت تريد فرض هيكل داخلي (أي.مثل وجود محرك واحد فقط) فأنت بحاجة إلى محرك آخر من الفئة المجردة/الأساسية يمكن أن يكون متخصصًا.

ثم يمكنك بناء سيارة رياضية من الأجزاء عن طريق تعيين عضو m_engine إلى فئة فرعية رياضية من المحرك، وما إلى ذلك

يحرر:لاحظ أنه في العالم الحقيقي، الشاحن التوربيني ليس جزءًا من المحرك، بل هو إضافة للمحرك، مع واجهة تحكم خاصة به...ولكن إذا كنت تريد تضمين أشياء مثل هذه في محرك الفيراري الخاص بك، فلا بأس، ما عليك سوى الترقية إلى الفئة الفرعية SportsCar لتحويل محركك الأساسي إلى محرك TurboEngine

ولكن سيكون من الأفضل إبقاء المكونات منفصلة - وبهذه الطريقة يمكنك ترقية الشاحن التوربيني الخاص بك (المدخل المزدوج مقابل المدخول الفردي، على سبيل المثال) دون استبدال المحرك بأكمله!

هناك الكثير من الطرق التي يمكن القيام بها.

أفضّل الحصول على setEngine() طريقة على Car, ، ثم الحصول على Ferrari استدعاء منشئ setEngine() وتمرير في مثيل أ TurboEngine.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top