سؤال

أواجه مشكلة في تصميم OO حيث انتهى بي الأمر مع رمز مكرر في فئتين مختلفتين. إليك ما يحدث:

في هذا المثال، أريد اكتشاف التصادم بين كائنات اللعبة.

لدي CollisionObject قاعدة تحمل أساليب مشتركة (مثل CheckForcollisionWith) و CollisionObjectBox، CollisionObjectCircle، CollisionObjectPolygon التي تمتد الفئة الأساسية.

يبدو هذا الجزء من التصميم موافق، لكن إليك ما يزعجني: الاتصال

aCircle checkForCollisionWith: aBox

سيؤدي أداء دائرة مقابل مربع التصادم داخل الدائرة الفرعية الدائرة. إلى الوراء،

aBox checkForCollisionWith: aCircle

سوف أداء مربع مقابل دائرة الاصطدام تحقق داخل مربع الفئة الفرعية مربع.

المشكلة هنا هي أن رمز التصادم مربع دائرة VS هو مكرر، لأنه في كل من الصندوق ودائرة الطبقات. هل هناك طريقة لتجنب ذلك، أو هل أقترب من هذه المشكلة بطريقة خاطئة؟ في الوقت الحالي، أنا أتميل نحو وجود فئة مساعد مع جميع التعليمات البرمجية المكررة واتصل بها من كائنات الأداء و ABOX لتجنب التكرارات. أنا فضولي إذا كان هناك حل أكثر أناقة OO لهذا، على الرغم من ذلك.

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

المحلول

كان لدي نفس المشكلة (العمل في الهدف ج)، وعملية حلية وجدتها لتحديد وظيفة خارجية لحل التصادم عندما أعرف بالفعل أنواع كلا الكائنتين.

على سبيل المثال، إذا كان لدي مستطيل ودائرة، فكل من تطبيق بروتوكول (نوع من الواجهة لهذه اللغة) ..

@protocol Shape

-(BOOL) intersects:(id<Shape>) anotherShape;
-(BOOL) intersectsWithCircle:(Circle*) aCircle;
-(BOOL) intersectsWithRectangle:(Rectangle*) aRectangle;

@end

تحديد intersectectswithcircle للاستطاعة، و intersectswithretanglectole للدائرة مثل هذا

-(BOOL) intersectsWithCircle:(Circle*) aCircle
{
    return CircleAndRectangleCollision(aCircle, self);
}

و ...

-(BOOL) intersectsWithRectangle:(Rectangle*) aRectangle
{
    return CircleAndRectangleCollision(self, aRectangle);
}

بالطبع لا يهاجم مشكلة اقتران الإرسال المزدوج، ولكن على الأقل يتجنب ذرتراض التعليمات البرمجية

نصائح أخرى

ما تريد يشار إليه باسم إرسال متعدد.

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

يمكن محاكات ذلك في لغات OOP الرئيسية، أو يمكنك استخدامه مباشرة إذا كنت تستخدم Lisp المشترك.

مثال Java في مقالة Wikipedia حتى يتعامل مع مشكلتك الدقيقة، وكشف الاصطدام.

إليك المزيفة في لغتهم "الحديثة":

abstract class CollisionObject {
    public abstract Collision CheckForCollisionWith(CollisionObject other);
}

class Box : CollisionObject {
    public override Collision CheckForCollisionWith(CollisionObject other) {
        if (other is Sphere) { 
            return Collision.BetweenBoxSphere(this, (Sphere)other);
        }
    }
}

class Sphere : CollisionObject {
    public override Collision CheckForCollisionWith(CollisionObject other) {
        if (other is Box) { 
            return Collision.BetweenBoxSphere((Box)other, this);
        }
    }
}

class Collision {
    public static Collision BetweenBoxSphere(Box b, Sphere s) { ... }
}

إليك ذلك في LISP المشترك:

(defmethod check-for-collision-with ((x box) (y sphere))
   (box-sphere-collision x y))

(defmethod check-for-collision-with ((x sphere) (y box))
   (box-sphere-collision y x))

(defun box-sphere-collision (box sphere)
    ...)

هذه مطبوعة نموذجية في تطوير OO. حاولت أيضا حل الاصطدامات بهذه الطريقة - فقط للفشل بفعالية.

هذه مسألة الملكية. هل تمتلك فئة الصندوق حقا منطق الاصطدام مع دائرة؟ لماذا ليس في الاتجاه الآخر جولة؟ النتيجة هي ازدواجية الكود أو تفويض رمز تصادم من الدائرة إلى المربع. كلاهما ليس نظيفا. Double Dispatch لا يحل هذا - نفس المشكلة مع الملكية ...

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

أنت لا تقول اللغة التي تستخدمها، لذلك أفترض أنها شيء مثل Java أو C #.

هذا هو الموقف الذي سيكون فيه MultiMethods حلا مثاليا، ولكن معظم اللغات لا تدعمها. الطريقة المعتادة لمحاكاةها هي مع بعض تباين نمط الزائر - انظر أي كتاب جيد على أنماط التصميم.

بدلا من ذلك، يمكنك الحصول على فئة تصويتية منفصلة تتحقق من التصادمات بين أزواج الكائنات، وإذا تصطدم كائنين، فهذا يتصل بالطرق المناسبة على الكائنات، على سبيل المثال bomb.explode () والتشغيل (). يمكن أن يكون للفئة طاولة بحث كبيرة مع كل نوع كائن على طول الصفوف والأعمدة، والإدخالات التي تعطي الأساليب للاتصال بكلتا كلا الكائنات.

ربما يمكنك الحصول على كائن تصادم يحتوي على طرق الاختبار لأنواع مختلفة من التصادمات. يمكن أن تعيد الطرق كائنات أخرى تحتوي على نقطة الاصطدام والمعلومات اللازمة الأخرى.

يجب عليك استخدام checkforcolleisionwith: acollisionoBject وعلى الرغم من أن جميع الكائنات الخاصة بك تمديد collectionObject يمكنك وضع كل منطق شائع هناك.

بدلا من ذلك يمكنك استخدام نمط تصميم الوفود لتبادل المنطق المشترك بين الطبقات المختلفة.

الخيار الأول: جعل الاصطدام اتجاهي. على سبيل المثال، إذا كان المربع ثابتا، فإنه لا يتحقق من تصادمه الخاص بأي شيء آخر؛ لكن الدائرة المتحركة يتحقق من التصادم مع المربع (وغيرها من الكائنات الثابتة). هذا غير مهتم لأن كل حياتنا نحن نعلم "ردود الفعل المتساوية والمعاكسة". pitfall: سوف تتحرك الكائنات مكررة التصادمات مع الكائنات المتحركة الأخرى.


الخيار الثاني: أعط كل كائن رقم معرف فريد. في طريقة فحص التصادم، تحقق فقط من التصادم إذا كان لدى المعلمة / الكائن الأول معرف أقل من المعلمة الثانية.

قل الصندوق يحتوي على معرف = 2 والدائرة لديه معرف = 5. بعد ذلك، سيتم تنفيذ "الصندوق مع دائرة"، نظرا لأن Box.ID <circle.id؛ ولكن بعد ذلك عندما تقوم الدائرة بتحقق من التصادمات، ستظهر "Circle Clipidides with box" فقط دون التحقق من التصادم، لأن التصادم قد تم فحصه بالفعل.

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