उपवर्ग प्रकार पूछने से बचने के लिए एक अच्छा डिजाइन अभ्यास क्या है?


11

मैंने पढ़ा है कि जब आपके कार्यक्रम को यह जानने की आवश्यकता होती है कि कोई वस्तु किस वर्ग की है, तो आमतौर पर डिजाइन दोष का संकेत मिलता है इसलिए मैं जानना चाहता हूं कि इसे संभालने के लिए एक अच्छा अभ्यास क्या है। मैं एक वर्ग आकृति को अलग-अलग उपवर्गों के साथ लागू कर रहा हूं जो इसे सर्कल, बहुभुज या आयत की तरह विरासत में मिला है और मुझे यह जानने के लिए अलग-अलग एल्गोरिदम हैं कि क्या एक वृत्त बहुभुज या आयत से टकराता है। फिर मान लें कि हमें शेप के दो उदाहरण मिल गए हैं और जानना चाहते हैं कि क्या कोई दूसरे से टकराता है, इस विधि में मुझे यह पता लगाना है कि मैं किस उपवर्ग प्रकार का ऑब्जेक्ट हूं, जिससे मैं यह जानने के लिए टकरा रहा हूं कि मुझे किस एल्गोरिथम को कॉल करना चाहिए लेकिन, यह एक है बुरा डिजाइन या अभ्यास? इस तरह से मैंने इसे हल किया है।

abstract class Shape {
  ShapeType getType();
  bool collide(Shape other);
}

class Circle : Shape {
  getType() { return Type.Circle; }

  bool collide(Shape other) {
    if(other.getType() == Type.Rect) {
      collideCircleRect(this, (Rect) other);     
    } else if(other.getType() == Type.Polygon) {
      collideCirclePolygon(this, (Polygon) other);
    }
  }
}

यह एक खराब डिज़ाइन पैटर्न है? उपवर्ग प्रकारों का पता लगाए बिना मैं इसे कैसे हल कर सकता हूं?


1
आप समाप्त करते हैं कि हर उदाहरण जैसे सर्कल हर दूसरे आकार-प्रकार को जानता है। इसलिए वे सभी किसी न किसी तरह से जुड़े हुए हैं। और जैसे ही आप एक नया आकार जोड़ते हैं, त्रिभुज की तरह, आप अंत में हर जगह त्रिकोण का समर्थन जोड़ते हैं। यह इस पर निर्भर करता है कि आप अधिक बार क्या बदलना चाहते हैं, क्या आप नए आकार जोड़ेंगे, यह डिजाइन खराब है। क्योंकि आपके पास समाधान फैला हुआ है - आपके त्रिकोण के समर्थन को हर जगह जोड़ा जाना चाहिए। इसके बजाय आपको अपनी Collisiondetection को एक अलग वर्ग में निकालना चाहिए, जो सभी प्रकारों और प्रतिनिधि के साथ काम कर सके।
द पॅकर


IMO यह प्रदर्शन आवश्यकताओं के लिए नीचे आता है। जितना अधिक विशिष्ट कोड होता है, उतना अधिक अनुकूलित किया जा सकता है और यह तेजी से चलेगा। इस विशेष मामले (इसे लागू किया, भी) में, प्रकार के लिए जाँच ठीक है क्योंकि अनुरूप टक्कर चेकों हो सकता है enourmously तेजी से एक सामान्य समाधान की तुलना में। लेकिन जब रनटाइम प्रदर्शन महत्वपूर्ण नहीं होता है, तो मैं हमेशा सामान्य / बहुरूपी दृष्टिकोण के साथ जाऊंगा।
Marstato

सभी के लिए धन्यवाद, मेरे मामले में प्रदर्शन महत्वपूर्ण है और मैं नए आकार नहीं जोड़ूंगा, हो सकता है कि मैं CollisionDetection दृष्टिकोण करूं, हालांकि मुझे अभी भी उपवर्ग के प्रकार को जानना था, क्या मुझे "टाइप getType ()" विधि रखनी चाहिए आकार या इसके बजाय CollisionDetection वर्ग में आकृति के साथ "उदाहरण" के कुछ प्रकार कर रहे हैं?
अलेजांद्रो

1
अमूर्त Shapeवस्तुओं के बीच कोई प्रभावी टकराव प्रक्रिया नहीं है । जब तक आप बाउंड्री पॉइंट्स के लिए टकराव की जाँच नहीं करते तब तक आपका तर्क अन्य ऑब्जेक्ट के इंटर्नल्स पर निर्भर करता है bool collide(x, y)(कंट्रोलर पॉइंट्स का सबसेट एक अच्छा ट्रेडऑफ़ हो सकता है)। अन्यथा आपको किसी भी प्रकार की जांच करने की आवश्यकता है - यदि वास्तव में सार की आवश्यकता है तो उत्पादन के Collisionप्रकार (वर्तमान अभिनेता के क्षेत्र के भीतर की वस्तुओं के लिए) सही दृष्टिकोण होना चाहिए।
shudder

जवाबों:


13

बहुरूपता

जब तक आप इसका उपयोग करते हैं getType()या ऐसा कुछ भी करते हैं, आप बहुरूपता का उपयोग नहीं कर रहे हैं।

मैं समझता हूं कि आपको यह जानना चाहिए कि आपके पास किस प्रकार का है। लेकिन कोई भी कार्य जिसे आप जानना चाहते हैं जबकि उसे वास्तव में कक्षा में नीचे धकेल दिया जाना चाहिए। फिर आप ही बताएं कि इसे कब करना है।

प्रक्रियात्मक कोड की जानकारी मिलती है फिर निर्णय लेता है। ऑब्जेक्ट-ओरिएंटेड कोड वस्तुओं को चीजें करने के लिए कहता है।
- एलेक शार्प

इस सिद्धांत को कहा जाता है, मत पूछो । इसका अनुसरण करने से आपको विवरणों को चारों ओर फैलाने और उन पर कार्य करने वाले तर्क बनाने में मदद नहीं मिलती है। ऐसा करने से एक कक्षा अंदर से बाहर हो जाती है। कक्षा के अंदर उस व्यवहार को रखना बेहतर होता है, ताकि कक्षा बदलने पर यह बदल सके।

encapsulation

आप मुझे बता सकते हैं कि किसी भी अन्य आकृतियों की कभी आवश्यकता नहीं होगी, लेकिन मुझे विश्वास नहीं है कि आपको और न ही आपको चाहिए।

निम्नलिखित एनकैप्सुलेशन का एक अच्छा प्रभाव यह है कि नए प्रकार जोड़ना आसान है क्योंकि उनका विवरण उस कोड में नहीं फैलता है जहां वे दिखाई देते हैं ifऔर switchतर्क। नए प्रकार के लिए कोड सभी एक जगह पर होना चाहिए।

एक प्रकार का अज्ञानी टकराव का पता लगाने की प्रणाली

मुझे दिखाते हैं कि मैं कैसे टकराव का पता लगाने वाला सिस्टम डिजाइन करूंगा जो कि प्रदर्शन करने वाला हो और किसी भी 2D आकृति के साथ काम करता हो।

यहाँ छवि विवरण दर्ज करें

कहते हैं कि आप इसे आकर्षित करने वाले थे। सरल लगता है। यह सभी मंडलियां हैं। यह एक सर्कल वर्ग बनाने के लिए लुभावना है जो टकराव को समझता है। समस्या यह है कि हमें सोचने की एक पंक्ति भेजती है जो 1000 सर्कल की आवश्यकता होने पर अलग हो जाती है।

हमें मंडलियों के बारे में नहीं सोचना चाहिए। हमें पिक्सल के बारे में सोचना चाहिए।

क्या होगा अगर मैंने आपको बताया कि इन लोगों को आकर्षित करने के लिए आप उसी कोड का उपयोग करते हैं, जिसका उपयोग आप यह पता लगाने के लिए कर सकते हैं कि जब वे स्पर्श करते हैं या यहां तक ​​कि उपयोगकर्ता किस पर क्लिक कर रहे हैं।

यहाँ छवि विवरण दर्ज करें

यहां मैंने प्रत्येक सर्कल को एक अद्वितीय रंग के साथ खींचा है (यदि आपकी आँखें काली रूपरेखा को देखने के लिए पर्याप्त हैं, तो बस उस पर ध्यान न दें)। इसका मतलब है कि इस छिपी हुई छवि के हर पिक्सेल ने इसे आकर्षित किया। एक हैशमैप उस अच्छी तरह से देखभाल करता है। आप वास्तव में इस तरह से बहुरूपता कर सकते हैं।

यह छवि आपको कभी भी उपयोगकर्ता को नहीं दिखानी होगी। आप इसे उसी कोड के साथ बनाते हैं जिसने पहले वाले को आकर्षित किया था। बस अलग-अलग रंगों से।

जब उपयोगकर्ता किसी सर्कल पर क्लिक करता है, तो मुझे पता होता है कि कौन सा सर्कल है क्योंकि केवल एक सर्कल उस रंग का है।

जब मैं किसी दूसरे के ऊपर एक चक्र खींचता हूं तो मैं हर पिक्सेल को जल्दी से पढ़ सकता हूं जो मैं उन्हें एक सेट में डंप करके अधिलेखित करने वाला हूं। जब मैंने सेट किया है तो हर सर्कल से टकरा गया है और अब मुझे केवल एक बार कॉल करना है ताकि टकराव की सूचना दी जा सके।

एक नया प्रकार: आयत

यह सब मंडलियों के साथ किया गया था, लेकिन मैं आपसे पूछता हूं: क्या यह आयतों के साथ कोई भिन्न कार्य करेगा?

कोई भी चक्र ज्ञान का पता लगाने वाली प्रणाली में लीक नहीं हुआ है। यह त्रिज्या, परिधि या केंद्र बिंदु के बारे में परवाह नहीं करता है। यह पिक्सेल और रंग की परवाह करता है।

इस टकराव प्रणाली का एकमात्र हिस्सा जिसे व्यक्तिगत आकृतियों में नीचे धकेलने की आवश्यकता होती है, वह एक अनूठा रंग है। इसके अलावा आकार केवल उनके आकृतियों को आकर्षित करने के बारे में सोच सकते हैं। यह वैसे भी वे अच्छे हैं।

अब जब आप टकराव तर्क लिखते हैं तो आपको परवाह नहीं है कि आपके पास क्या उपप्रकार है। आप इसे टकराने के लिए कहते हैं और यह आपको बताता है कि यह किस आकृति के नीचे मिला है जो इसे खींचने का दिखावा कर रहा है। प्रकार जानने की जरूरत नहीं है। और इसका मतलब है कि आप अन्य वर्गों में कोड को अपडेट किए बिना आप जितने चाहें उतने उप प्रकार जोड़ सकते हैं।

कार्यान्वयन विकल्प

वास्तव में, यह एक अद्वितीय रंग होने की जरूरत नहीं है। यह वास्तविक वस्तु संदर्भ हो सकता है और अप्रत्यक्ष स्तर को बचा सकता है। लेकिन इस जवाब में तैयार होने पर वे उतने अच्छे नहीं दिखेंगे।

यह केवल एक कार्यान्वयन उदाहरण है। वहाँ निश्चित रूप से अन्य हैं। यह दिखाने के लिए क्या था कि आप इन आकार उप-छोरों को अपनी एकल ज़िम्मेदारी के साथ बेहतर तरीके से पूरी प्रणाली में काम करने दें। संभावित रूप से तेज़ और कम मेमोरी सघन समाधान हैं, लेकिन अगर वे मुझे उप-योगों का ज्ञान फैलाने के लिए मजबूर करते हैं तो मुझे प्रदर्शन लाभ के साथ भी उनका उपयोग करने के लिए घृणा होगी। जब तक मुझे स्पष्ट रूप से उनकी आवश्यकता नहीं होगी मैं उनका उपयोग नहीं करूंगा।

दोहरा प्रेषण

अब तक मैंने दोहरे प्रेषण को पूरी तरह से अनदेखा कर दिया है । मैंने ऐसा इसलिए किया क्योंकि मैं कर सकता था। जब तक टकराव वाले तर्क की परवाह नहीं की जाती है, तो दो प्रकार के टकराव की जरूरत नहीं है। यदि आपको इसकी आवश्यकता नहीं है, तो इसका उपयोग न करें। यदि आपको लगता है कि आपको इसकी आवश्यकता हो सकती है, तो जब तक आप यह कर सकते हैं तब तक इससे निपटना चाहिए। इस रवैये को YAGNI कहा जाता है ।

यदि आप तय करते हैं कि आपको वास्तव में विभिन्न प्रकार के टकरावों की आवश्यकता है, तो अपने आप से पूछें कि क्या n आकार के उपप्रकारों को वास्तव में n 2 प्रकार के टकरावों की आवश्यकता है। अब तक मैंने एक और आकार का उपप्रकार जोड़ना आसान बनाने के लिए वास्तव में कड़ी मेहनत की है। मैं इसे दोहरे प्रेषण कार्यान्वयन के साथ खराब नहीं करना चाहता जो कि हलकों को यह जानने के लिए मजबूर करता है कि वर्ग मौजूद हैं।

वैसे भी कितने प्रकार के टकराव हैं? एक छोटी सी अटकलें (एक खतरनाक चीज) लोचदार टकराव (उछालभरी), इनैलास्टिक (चिपचिपा), ऊर्जावान (अन्वेषण), और विनाशकारी (डैमगी) को जोड़ती है। वहाँ अधिक हो सकता है लेकिन अगर यह n 2 से कम है तो हमारे टकरावों को खत्म नहीं होने देता।

इसका मतलब यह है कि जब मेरा टारपीडो किसी ऐसी चीज से टकराता है जो नुकसान को स्वीकार करती है तो उसे पता नहीं चलता है कि यह एक अंतरिक्ष जहाज को मारती है। इसे केवल यह बताना है, "हा हा! आपने नुकसान के 5 अंक लिए।"

नुकसान पहुंचाने वाली चीजें नुकसान संदेशों को नुकसान पहुंचाने वाले संदेश भेजती हैं। इस तरह से आप अन्य आकृतियों को नए आकार के बारे में बताए बिना नए आकार जोड़ सकते हैं। आप केवल नए प्रकार के टकरावों के आसपास फैल रहे हैं।

अंतरिक्ष जहाज वापस टॉर को भेज सकता है "हा हा! आपने नुकसान के 100 अंक ले लिए।" के रूप में अच्छी तरह से "आप अब मेरे पतवार से चिपके हुए हैं"। और टॉरपी वापस भेज सकता है "ठीक है, मैं ऐसा कर रहा हूं ताकि मेरे बारे में भूल जाओ"।

किसी भी बिंदु पर या तो बिल्कुल नहीं पता कि प्रत्येक क्या है। वे बस जानते हैं कि टकराव इंटरफ़ेस के माध्यम से एक दूसरे से कैसे बात करें।

अब यकीन है, डबल प्रेषण आपको चीजों को इससे अधिक अंतर से नियंत्रित करने देता है लेकिन क्या आप वास्तव में ऐसा चाहते हैं ?

यदि आप कम से कम कृपया इस बारे में सोचें कि क्या आकार के टकराव के माध्यम से दोहरी प्रेषण होता है, तो कोई आकृति वास्तविक आकार के कार्यान्वयन पर नहीं। इसके अलावा, टकराव का व्यवहार एक ऐसी चीज है जिस पर आप निर्भरता के रूप में इंजेक्शन लगा सकते हैं और उस निर्भरता को सौंप सकते हैं।

प्रदर्शन

प्रदर्शन हमेशा महत्वपूर्ण होता है। लेकिन इसका मतलब यह नहीं है कि यह हमेशा एक मुद्दा है। टेस्ट प्रदर्शन। सिर्फ अटकलें मत लगाओ। प्रदर्शन के नाम पर बाकी सबकुछ त्यागने से आमतौर पर किसी भी तरह से वेध कोड नहीं होता है।



+1 के लिए "आप मुझे बता सकते हैं कि किसी भी अन्य आकृतियों की कभी ज़रूरत नहीं होगी, लेकिन मुझे विश्वास नहीं है कि आप और न ही आपको चाहिए।"
ट्यूलेंस कोरडोवा

यदि यह प्रोग्राम आरेखण आकार के बारे में नहीं है, लेकिन विशुद्ध रूप से गणितीय गणनाओं के बारे में है, तो पिक्सेल के बारे में सोचना आपको कहीं नहीं मिलेगा। इस उत्तर का तात्पर्य है कि आपको कथित वस्तु-उन्मुख शुद्धता के लिए सब कुछ त्याग देना चाहिए। इसमें एक विरोधाभास भी शामिल है: आप पहले कहते हैं कि हमें इस विचार पर अपने पूरे डिजाइन को आधार बनाना चाहिए कि हमें भविष्य में और अधिक प्रकार के आकार की आवश्यकता हो सकती है, फिर आप "YAGNI" कहते हैं। अंत में, आप उपेक्षा करते हैं कि प्रकारों को जोड़ना आसान बना देता है अक्सर इसका मतलब है कि संचालन को जोड़ना कठिन है, जो बुरा है यदि प्रकार पदानुक्रम अपेक्षाकृत स्थिर है, लेकिन संचालन बहुत बदल जाता है।
क्रिश्चियन हैकल

7

समस्या का वर्णन ऐसा लगता है जैसे आपको मल्टीमिथोड्स (उर्फ मल्टीपल डिस्पैच) का उपयोग करना चाहिए , इस विशेष मामले में - डबल प्रेषण । पहला उत्तर यह था कि रास्टर रेंडरिंग में टकराने वाली आकृतियों के साथ उदारतापूर्वक व्यवहार कैसे किया जाए, लेकिन मुझे विश्वास था कि ओपी चाहता था कि "वेक्टर" समाधान या शायद पूरी समस्या को शाप के संदर्भ में सुधार दिया गया है, जो ओओपी स्पष्टीकरण में शास्त्रीय उदाहरण है।

यहां तक ​​कि विकिपीडिया के लेख में समान टकराव रूपक का उपयोग किया गया है, मुझे केवल उद्धृत करें (पायथन में कुछ अन्य भाषाओं की तरह बिल्ट-इन मल्टीमिथोड्स नहीं हैं):

@multimethod(Asteroid, Asteroid)
def collide(a, b):
    """Behavior when asteroid hits asteroid"""
    # ...define new behavior...
@multimethod(Asteroid, Spaceship)
def collide(a, b):
    """Behavior when asteroid hits spaceship"""
    # ...define new behavior...
# ... define other multimethod rules ...

तो, अगला सवाल यह है कि अपनी प्रोग्रामिंग भाषा में मल्टीमिथोड्स के लिए समर्थन कैसे प्राप्त करें।



हां, मल्टीपल डिस्पैच उर्फ ​​मल्टीमिथोड्स का विशेष मामला, उत्तर में जोड़ा गया
रोमन सूसी

5

इस समस्या को दो स्तरों पर पुनः डिज़ाइन करने की आवश्यकता है।

सबसे पहले, आपको आकृतियों के बाहर आकृतियों के बीच टकराव का पता लगाने के लिए तर्क को बाहर निकालना होगा। ऐसा इसलिए है कि जब भी आपको मॉडल में एक नया आकार जोड़ने की आवश्यकता होती है, तो आप हर बार ओसीपी का उल्लंघन नहीं करेंगे । कल्पना कीजिए कि आपके पास पहले से ही सर्कल, स्क्वायर और आयत परिभाषित हैं। फिर आप इसे इस तरह से कर सकते हैं:

class ShapeCollisionDetector
{
    public void DetectCollisionCircleCircle(Circle firstCircle, Circle secondCircle)
    { 
        //Code that detects collision between two circles
    }

    public void DetectCollisionCircleSquare(Circle circle, Square square)
    {
        //Code that detects collision between circle and square
    }

    public void DetectCollisionCircleRectangle(Circle circle, Rectangle rectangle)
    {
        //Code that detects collision between circle and rectangle
    }

    public void DetectCollisionSquareSquare(Square firstSquare, Square secondSquare)
    {
        //Code that detects collision between two squares
    }

    public void DetectCollisionSquareRectangle(Square square, Rectangle rectangle)
    {
        //Code that detects collision between square and rectangle
    }

    public void DetectCollisionRectangleRectangle(Rectangle firstRectangle, Rectangle secondRectangle)
    { 
        //Code that detects collision between two rectangles
    }
}

इसके बाद, आपको उस कॉल के आकार के आधार पर उपयुक्त विधि के लिए व्यवस्था करनी होगी। आप ऐसा कर सकते हैं कि बहुरूपता और आगंतुक पैटर्न का उपयोग करना । इसे प्राप्त करने के लिए, हमारे पास उचित ऑब्जेक्ट मॉडल होना चाहिए। सबसे पहले, सभी आकृतियों को एक ही इंटरफ़ेस का पालन करना होगा:

    interface IShape
{
    void DetectCollision(IShape shape);
    void Accept (ShapeVisitor visitor);
}

अगला, हमारे पास एक अभिभावक आगंतुक वर्ग होना चाहिए:

    abstract class ShapeVisitor
{
    protected ShapeCollisionDetector collisionDetector = new ShapeCollisionDetector();

    abstract public void VisitCircle (Circle circle);

    abstract public void VisitSquare(Square square);

    abstract public void VisitRectangle(Rectangle rectangle);

}

मैं इंटरफ़ेस के बजाय यहां एक वर्ग का उपयोग कर रहा हूं, क्योंकि मुझे प्रत्येक आगंतुक ऑब्जेक्ट की विशेषता की आवश्यकता है ShapeCollisionDetector

IShapeइंटरफ़ेस का प्रत्येक कार्यान्वयन उचित आगंतुक को तुरंत करेगा और Acceptउस ऑब्जेक्ट की उपयुक्त विधि को कॉल करेगा जिसे कॉलिंग ऑब्जेक्ट इस तरह से बातचीत करता है:

    class Circle : IShape
{
    public void DetectCollision(IShape shape)
    {
        CircleVisitor visitor = new CircleVisitor(this);
        shape.Accept(visitor);
    }

    public void Accept(ShapeVisitor visitor)
    {
        visitor.VisitCircle(this);
    }
}

    class Rectangle : IShape
{
    public void DetectCollision(IShape shape)
    {
        RectangleVisitor visitor = new RectangleVisitor(this);
        shape.Accept(visitor);
    }

    public void Accept(ShapeVisitor visitor)
    {
        visitor.VisitRectangle(this);
    }
}

और विशिष्ट आगंतुक इस तरह दिखेंगे:

    class CircleVisitor : ShapeVisitor
{
    private Circle Circle { get; set; }

    public CircleVisitor(Circle circle)
    {
        this.Circle = circle;
    }

    public override void VisitCircle(Circle circle)
    {
        collisionDetector.DetectCollisionCircleCircle(Circle, circle);
    }

    public override void VisitSquare(Square square)
    {
        collisionDetector.DetectCollisionCircleSquare(Circle, square);
    }

    public override void VisitRectangle(Rectangle rectangle)
    {
        collisionDetector.DetectCollisionCircleRectangle(Circle, rectangle);
    }
}

    class RectangleVisitor : ShapeVisitor
{
    private Rectangle Rectangle { get; set; }

    public RectangleVisitor(Rectangle rectangle)
    {
        this.Rectangle = rectangle;
    }

    public override void VisitCircle(Circle circle)
    {
        collisionDetector.DetectCollisionCircleRectangle(circle, Rectangle);
    }

    public override void VisitSquare(Square square)
    {
        collisionDetector.DetectCollisionSquareRectangle(square, Rectangle);
    }

    public override void VisitRectangle(Rectangle rectangle)
    {
        collisionDetector.DetectCollisionRectangleRectangle(Rectangle, rectangle);
    }
}

इस तरह, आपको हर बार एक नया आकार जोड़ने के लिए आकार की कक्षाओं को बदलने की आवश्यकता नहीं होती है, और उचित टकराव का पता लगाने की विधि को कॉल करने के लिए आपको आकार के प्रकार की जांच करने की आवश्यकता नहीं है।

इस समाधान का एक दोष यह है कि यदि आप एक नया आकार जोड़ते हैं, तो आपको उस आकार (जैसे VisitTriangle(Triangle triangle)) के लिए विधि के साथ ShapeVisitor वर्ग का विस्तार करना होगा, और परिणामस्वरूप, आपको अन्य सभी आगंतुकों में उस पद्धति को लागू करना होगा। हालाँकि, चूंकि यह विस्तार है, इस अर्थ में कि कोई मौजूदा तरीके नहीं बदले गए हैं, लेकिन केवल नए जोड़े गए हैं, यह OCP का उल्लंघन नहीं करता है , और कोड ओवरहेड न्यूनतम है। इसके अलावा, वर्ग का उपयोग करके ShapeCollisionDetector, आप SRP के उल्लंघन से बचते हैं और आप कोड अतिरेक से बचते हैं।


5

आपकी मूल समस्या यह है कि अधिकांश आधुनिक OO प्रोग्रामिंग भाषाओं में फ़ंक्शन ओवरलोडिंग डायनेमिक बाइंडिंग के साथ काम नहीं करता है (यानी संकलन समय पर फ़ंक्शन के प्रकार का निर्धारण किया जाता है)। आपको जिस चीज़ की आवश्यकता होगी वह एक आभासी विधि कॉल है जो केवल एक के बजाय दो वस्तुओं पर आभासी है। ऐसी विधियों को बहु-विधियाँ कहा जाता है । हालाँकि, जावा, C ++ आदि भाषाओं में इस व्यवहार का अनुकरण करने के तरीके हैं । यह वह जगह है जहाँ डबल प्रेषण बहुत काम आता है।

मूल विचार यह है कि आप दो बार बहुरूपता का उपयोग करते हैं। जब दो आकृतियाँ टकराती हैं, तो आप बहुरूपता के माध्यम से किसी एक पिंड की सही टकराव विधि को कॉल कर सकते हैं और जेनेरिक आकार के प्रकार के अन्य ऑब्जेक्ट को पास कर सकते हैं। तथाकथित विधि में आप जानते हैं कि क्या यह वस्तु एक वृत्त, आयत या जो कुछ भी है। आप फिर पास की गई आकृति ऑब्जेक्ट पर टकराव विधि को कॉल करते हैं और इसे इस ऑब्जेक्ट को पास करते हैं । यह दूसरा कॉल फिर पॉलीमॉर्फिज़्म के माध्यम से सही ऑब्जेक्ट प्रकार को पाता है।

abstract class Shape {
  bool collide(Shape other);
  bool collide(Rect other);
  bool collide(Circle other);
}

class Circle : Shape {

  bool collide(Shape other) {
    return other.collide(this);
  }

  bool collide(Rect other) {
    // algorithm to detect collision between Circle and Rect
  }

  // ...
}

class Rect : Shape {

  bool collide(Shape other) {
    return other.collide(this);
  }

  bool collide(Circle other) {
    // algorithm to detect collision between Circle and Rect
  }

  // ...
}

हालाँकि, इस तकनीक का एक बड़ा दोष यह है कि पदानुक्रम में प्रत्येक वर्ग को सभी भाई-बहनों के बारे में जानना होता है। यदि कोई नया आकार बाद में जोड़ा जाता है, तो यह उच्च रखरखाव का बोझ रखता है।


2

शायद यह इस समस्या का सबसे अच्छा तरीका नहीं है

मैथ बिहिंग शेप की टक्कर आकृति संयोजनों के लिए विशेष रूप से है। इसका मतलब है कि आपके द्वारा आवश्यक उप-रूटीन की संख्या आपके द्वारा समर्थित सिस्टम के आकार की संख्या का वर्ग है। आकार की टकराव वास्तव में आकृतियों पर परिचालन नहीं होते हैं, बल्कि ऐसे ऑपरेशन होते हैं जो आकार को मापदंडों के रूप में लेते हैं।

ऑपरेटर अधिभार रणनीति

यदि आप अंतर्निहित गणित समस्या को सरल नहीं कर सकते हैं, तो मैं ऑपरेटर अधिभार दृष्टिकोण को फिर से शामिल करूंगा। कुछ इस तरह:

 public final class ShapeOp 
 {
     static { ... }

     public static boolean collision( Shape s1, Shape s2 )  { ... }
     public static boolean collision( Point p1, Point p2 ) { ... }
     public static boolean collision( Point p1, Square s1 ) { ... }
     public static boolean collision( Point p1, Circle c1 ) { ... }
     public static boolean collision( Point p1, Line l1 ) { ... }
     public static boolean collision( Square s1, Point p2 ) { ... }
     public static boolean collision( Square s1, Square s2 ) { ... }
     public static boolean collision( Square s1, Circle c1 ) { ... }
     public static boolean collision( Square s1, Line l1 ) { ... }
     (...)

स्टैटिक इंटेलाइज़र पर मैं जेनेरिक टक्कर (डायमेंशन s1, शेप s2) मेथड पर डायनामिक डिस्पैचर को लागू करने के तरीकों के मैप बनाने के लिए रिफ्लेक्शन का उपयोग करूँगा। स्थिर इंटेलाइज़र के पास लापता टक्कर कार्यों का पता लगाने और उन्हें रिपोर्ट करने के लिए एक तर्क भी हो सकता है, जिससे कक्षा को लोड करने से इनकार कर दिया जा सकता है।

यह C ++ ऑपरेटर ओवरलोड के समान है। C ++ में ऑपरेटर ओवरलोड बहुत भ्रमित करने वाला है क्योंकि आपके पास प्रतीकों का एक निश्चित सेट है जिसे आप ओवरलोड कर सकते हैं। हालांकि, अवधारणा बहुत दिलचस्प है और इसे स्थिर कार्यों के साथ दोहराया जा सकता है।

इस दृष्टिकोण का उपयोग करने का कारण यह है कि टकराव एक वस्तु पर एक ऑपरेशन नहीं है। टकराव एक बाहरी ऑपरेशन है जो दो मनमानी वस्तुओं के बारे में कुछ संबंध कहता है। इसके अलावा, स्टैटिक इनिशियलाइज़र यह जांचने में सक्षम होगा कि क्या मैं कुछ टक्कर फ़ंक्शन को याद करता हूं।

यदि संभव हो तो अपनी गणित की समस्या को सरल बनाएं

जैसा कि मैंने उल्लेख किया है, टकराव कार्यों की संख्या आकृति प्रकारों की संख्या का वर्ग है। इसका मतलब यह है कि केवल 20 आकृतियों वाले सिस्टम में आपको 400 मार्गों की आवश्यकता होगी, जिसमें 21 आकार 441 और इतने पर होंगे। यह आसानी से एक्स्टेंसिबल नहीं है।

लेकिन आप अपने गणित को सरल बना सकते हैं । टकराव फ़ंक्शन को विस्तारित करने के बजाय आप प्रत्येक आकृति को rasterize या triangulate कर सकते हैं। इस तरह से टक्कर इंजन को एक्स्टेंसिबल होने की आवश्यकता नहीं है। टकराव, दूरी, अंतर, विलय और कई अन्य कार्य सार्वभौमिक होंगे।

त्रिकोणाकार

क्या आपने सबसे अधिक 3 डी पैकेज और गेम को सब कुछ त्रिकोणीय रूप से देखा है? यह गणित को सरल बनाने के रूपों में से एक है। यह 2d आकृतियों पर भी लागू होता है। पोल को त्रिभुजित किया जा सकता है। सर्कल्स और स्प्लिन्स को पॉलीगन्स के लिए अनुमानित किया जा सकता है।

फिर से ... आप एक एकल टकराव समारोह होगा। आपकी कक्षा तब बन जाती है:

public class Shape 
{
    public Triangle[] triangulate();
}

और आपके ऑपरेशन:

public final class ShapeOp
{
    public static boolean collision( Triangle[] shape1, Triangle[] shape2 )
}

सरल यह नहीं है?

रेस्टराइज़

आप अपने आकार को एक टकराव के कार्य के लिए व्यवस्थित कर सकते हैं।

रेखापुंज एक कट्टरपंथी समाधान लग सकता है, लेकिन आपके आकार के टकराव को कितना सटीक होना चाहिए, इसके आधार पर सस्ती और तेज हो सकती है। यदि उन्हें सटीक होने की आवश्यकता नहीं है (जैसे खेल में) तो आपके पास कम रिज़ॉल्यूशन बिटमैप हो सकते हैं। अधिकांश अनुप्रयोगों को गणित में पूर्ण सटीकता की आवश्यकता नहीं होती है।

अनुमान काफी अच्छे हो सकते हैं। जीव विज्ञान सिमुलेशन के लिए एंटोन सुपर कंप्यूटर एक उदाहरण है। इसके गणित में बहुत सारे क्वांटम प्रभाव दिखाई देते हैं जिनकी गणना करना कठिन है और अब तक किए गए सिमुलेशन वास्तविक दुनिया में किए गए प्रयोगों के अनुरूप हैं। गेम इंजन और रेंडरिंग पैकेज में उपयोग किए जाने वाले PBR कंप्यूटर ग्राफिक्स मॉडल सरलीकरण करते हैं जो प्रत्येक फ्रेम को प्रस्तुत करने के लिए आवश्यक कंप्यूटर शक्ति को कम करता है। वास्तव में phisically सटीक नहीं है, लेकिन नग्न आंखों के प्रति आश्वस्त होने के लिए पर्याप्त है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.