घटक-आधारित डिज़ाइन: ऑब्जेक्ट्स इंटरैक्शन को संभालना


9

मुझे यकीन नहीं है कि वास्तव में ऑब्जेक्ट एक घटक आधारित डिज़ाइन में अन्य वस्तुओं के लिए कैसे करते हैं।

कहो मेरे पास एक Objक्लास है। मैं करता हूँ:

Obj obj;
obj.add(new Position());
obj.add(new Physics());

फिर मैं कैसे एक और वस्तु ले सकता हूं न केवल गेंद को आगे बढ़ाएं बल्कि उन भौतिकी को लागू किया जाए। मैं कार्यान्वयन विवरण की तलाश नहीं कर रहा हूं, बल्कि सारगर्भित है कि वस्तुएं कैसे संवाद करती हैं। इकाई आधारित डिज़ाइन में, आपके पास बस हो सकता है:

obj1.emitForceOn(obj2,5.0,0.0,0.0);

एक घटक संचालित डिजाइन पर एक बेहतर समझ पाने के लिए कोई भी लेख या स्पष्टीकरण और बुनियादी चीजें कैसे करें यह वास्तव में सहायक होगा।

जवाबों:


10

यह आमतौर पर संदेशों का उपयोग करके किया जाता है। आप इस साइट पर अन्य प्रश्नों के बहुत सारे विवरण पा सकते हैं, जैसे यहाँ या वहाँ

अपने विशिष्ट उदाहरण का उत्तर देने के लिए, जाने का एक तरीका एक छोटी Messageकक्षा को परिभाषित करना है जिसे आपकी वस्तुएं संसाधित कर सकती हैं, जैसे:

struct Message
{
    Message(const Objt& sender, const std::string& msg)
        : m_sender(&sender)
        , m_msg(msg) {}
    const Obj* m_sender;
    std::string m_msg;
};

void Obj::Process(const Message& msg)
{
    for (int i=0; i<m_components.size(); ++i)
    {
        // let components do some stuff with msg
        m_components[i].Process(msg);
    }
}

इस तरह आप Objघटक से संबंधित विधियों के साथ क्लास इंटरफ़ेस "प्रदूषणकारी" नहीं हैं । कुछ घटक संदेश को संसाधित करना चुन सकते हैं, कुछ इसे अनदेखा कर सकते हैं।

आप इस विधि को सीधे किसी अन्य ऑब्जेक्ट से कॉल करके शुरू कर सकते हैं:

Message msg(obj1, "EmitForce(5.0,0.0,0.0)");
obj2.ProcessMessage(msg);

इस मामले में, obj2के Physicsसंदेश लेने, और जो प्रसंस्करण यह करने की जरूरत है क्या करेंगे। जब किया, यह या तो होगा:

  • स्वयं को "सेटपोजिशन" संदेश भेजें, जो Positionघटक उठाएगा ;
  • या सीधे Positionसंशोधनों के लिए घटक का उपयोग करें (शुद्ध घटक-आधारित डिज़ाइन के लिए काफी गलत है, क्योंकि आप यह नहीं मान सकते हैं कि प्रत्येक वस्तु में एक Positionघटक है, लेकिन Positionघटक एक आवश्यकता हो सकती है Physics)।

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

आपको शायद बाद में और अधिक उन्नत प्रणाली के लिए जाना होगा: अतुल्यकालिक संदेश कतार, वस्तुओं के समूह को संदेश भेजना, प्रति-घटक पंजीकरण / संदेशों से अपंजीकृत करना आदि।

Messageजैसा कि ऊपर दिखाया, लेकिन क्रम में प्रसंस्करण तार वास्तव में कुशल नहीं है वर्ग एक सरल स्ट्रिंग के लिए एक सामान्य कंटेनर हो सकता है। आप सामान्य मूल्यों के एक कंटेनर के लिए जा सकते हैं: तार, पूर्णांक, फ़्लोट्स ... एक नाम या बेहतर अभी तक, एक आईडी के साथ, विभिन्न प्रकार के संदेशों को अलग करने के लिए। या आप विशिष्ट आवश्यकताओं को पूरा करने के लिए एक बेस क्लास भी प्राप्त कर सकते हैं। यदि आप ऐसा करते हैं, तो आपके मामले में, आप एक कल्पना कर सकते हैं और वांछित बल वेक्टर-बटू को आरटीटीआई की रनटाइम लागत EmitForceMessageसे Messageजोड़ सकते हैं।


3
मैं सीधे पहुँचने वाले घटकों की "गैर शुद्धता" के बारे में चिंता नहीं करता। घटकों का उपयोग कार्यात्मक और डिजाइन की जरूरतों को पूरा करने के लिए किया जाता है, शिक्षाविदों के लिए नहीं। आप जाँचना चाहते हैं कि एक घटक मौजूद है (उदाहरण के लिए, चेक रिटर्न वैल्यू, घटक कॉल के लिए शून्य नहीं है)।
शॉन मिडिल्डिच

मैंने हमेशा यह सोचा है कि आपने पिछली बार RTTI का उपयोग करते हुए कहा था, लेकिन इतने सारे लोगों ने RTTI के बारे में बहुत सारी बुरी बातें
कही हैं

@SeanMiddleditch निश्चित रूप से, मैं इसे इस तरह से करूंगा, बस यह उल्लेख करते हुए कि यह स्पष्ट करने के लिए कि आपको एक ही इकाई के अन्य घटकों को एक्सेस करते समय हमेशा यह देखना चाहिए कि आप क्या कर रहे हैं।
लॉरेंट कौविदो

@ मायो कंपाइलर ने आरटीटीआई लागू किया है और यह एक अड़चन बन dynamic_cast सकता है , लेकिन मैं अब इस बारे में चिंता नहीं करूंगा। आप अभी भी इसे बाद में अनुकूलित कर सकते हैं यदि यह एक मुद्दा बन जाता है। CRC- आधारित वर्ग के पहचानकर्ता एक आकर्षण की तरह काम करते हैं।
लॉरेंट कौविदो

Inttemplate <typename T> uint32_t class_id () {स्थिर uint32_t v; वापसी (uint32_t) & v; }} - कोई आरटीटीआई की जरूरत नहीं।
अरूल

3

आपने जो दिखाया, उसके समान एक समस्या को हल करने के लिए मैंने कुछ विशिष्ट घटक संचालकों को जोड़ा और कुछ प्रकार के इवेंट रिज़ॉल्यूशन सिस्टम को जोड़ा।

इसलिए, आपके "भौतिकी" ऑब्जेक्ट के मामले में, जब इसे इनिशियलाइज़ किया जाता है, तो यह खुद को भौतिकी के एक केंद्रीय प्रबंधक के लिए जोड़ देगा। गेम लूप में, इस तरह के प्रबंधकों का अपना अपडेट स्टेप होता है, इसलिए जब इस PhysicsManager को अपडेट किया जाता है तो यह सभी फिजिक्स इंटरैक्शन की गणना करता है और उन्हें एक इवेंट कतार में जोड़ता है।

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

इस विधि का लाभ:

  • वैचारिक रूप से, पालन करने के लिए वास्तव में सरल है।
  • आपको क्वाडट्रेस या जो कुछ भी आपको आवश्यकता होगी, जैसे विशिष्ट अनुकूलन के लिए कमरा देता है।
  • यह समाप्त होता है वास्तव में "प्लग एंड प्ले"। भौतिक विज्ञान वाली वस्तुएँ गैर-भौतिक वस्तुओं के साथ परस्पर क्रिया नहीं करती हैं क्योंकि वे प्रबंधक के लिए मौजूद नहीं होती हैं।

विपक्ष:

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

आशा है कि ये आपकी मदद करेगा।

पुनश्च: यदि किसी के पास इसे हल करने के लिए एक क्लीनर / बेहतर तरीका है, तो मैं वास्तव में इसे सुनना चाहूंगा।


1
obj->Message( "Physics.EmitForce 0.0 1.1 2.2" );
// and some variations such as...
obj->Message( "Physics.EmitForce", "0.0 1.1 2.2" );
obj->Message( "Physics", "EmitForce", "0.0 1.1 2.2" );

इस डिजाइन पर ध्यान देने योग्य कुछ बातें:

  • घटक का नाम पहला पैरामीटर है - यह संदेश पर बहुत अधिक कोड काम करने से बचने के लिए है - हम यह नहीं जान सकते हैं कि किसी भी संदेश को कौन से घटक ट्रिगर कर सकते हैं - और हम उन सभी को 90% विफलता के साथ एक संदेश चबाना नहीं चाहते हैं वह दर जो बहुत सारी अनावश्यक शाखाओं में परिवर्तित हो जाती है और स्ट्रैम्प का निर्माण करती है।
  • संदेश का नाम दूसरा पैरामीटर है।
  • पहली डॉट (# 1 और # 2 में) आवश्यक नहीं है, यह सिर्फ पढ़ने को आसान बनाने के लिए है (लोगों के लिए, कंप्यूटर नहीं)।
  • यह sscanf, iostream, आप-नाम-संगत है। कोई वाक्यगत चीनी जो संदेश के प्रसंस्करण को सरल बनाने के लिए कुछ नहीं करती है।
  • एक स्ट्रिंग पैरामीटर: देशी प्रकारों को पास करना स्मृति आवश्यकताओं के संदर्भ में सस्ता नहीं है, क्योंकि आपको अपेक्षाकृत अज्ञात प्रकार के मापदंडों की एक अज्ञात संख्या का समर्थन करना होगा।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.