सी ++: पट्टा पैटर्न

दूसरे दिन एक सहकर्मी ने इस तरह का काम किया:

“दो प्रकार की वस्तुएं हैं - मानव और कुत्ता । मानव का अपना कोई कुत्ता हो सकता है (या अपना नहीं हो सकता)। कुत्ते के पास कुछ मेजबान (या नहीं हो सकता है) हो सकता है। यह स्पष्ट है कि यदि टाइप ह्यूमन का कोई ऑब्जेक्ट डॉग के कुछ ऑब्जेक्ट का मालिक है, तो टाइप डॉग के इस ऑब्जेक्ट के लिए, यह टाइप ह्यूमन का ऑब्जेक्ट है, जो केवल मास्टर और केवल वह है। और डॉग को पता होना चाहिए कि उसका मानव कौन है , और इसके विपरीत। आप इसे कैसे लागू करेंगे? ”

ऐसा लगता है कि सब कुछ सरल है - चलो मानव और कुत्ते में कक्षाओं में एक दूसरे को दो संकेत मिलते हैं और बात टोपी में है। लेकिन इस उद्यम के कार्यान्वयन ने मुझे इस विचार के लिए प्रेरित किया, यह मुझे लगता है, एक नए डिजाइन पैटर्न के लिए।

और यदि कोई टेम्प्लेट नहीं है, तो कम से कम एक सी ++ मुहावरे जो आपको स्थिर प्रकार के नियंत्रण और उपयोगी "गुडी" के एक जोड़े के साथ कार्यक्रम में द्विदिश लिंक का उपयोग करने की अनुमति देता है।

नोट: शब्द "लिंक" का उपयोग लेख में "कनेक्शन" के अर्थ में किया जाता है, न कि "सी ++ लिंक" के अर्थ में।

सबसे पहले, आइए देखें कि संकेत के साथ पहला विचार क्या बुरा है।

पट्टा v.1 ("माथे पर")


class Dog; class Human; class Dog { Human* link; public: Dog(): link(NULL) {} void SetLink(Human* l) { link = l; } const Human* GetLink() const { return link; } } class Human { Dog* link; public: Human(): link(NULL) {} void SetLink(Dog* l) { link = l; } const Dog* GetLink() const { return link; } } 


ऐसा कार्यान्वयन हमें समस्या को हल करने की अनुमति देता है, जैसा कि वे कहते हैं, "माथे में":
 Human *h = new Human(); Dog *d = new Dog(); h->SetLink(d); d->SetLink(h); 


हालाँकि, इसके कई स्पष्ट नुकसान हैं:
  1. आप बैकलिंक सेट करना भूल सकते हैं: d->SetLink(h)
  2. आप गलती से किसी अन्य ऑब्जेक्ट के लिए एक बैकवर्ड लिंक सेट कर सकते हैं: d->SetLink(h2)
  3. संबंधित वस्तुओं में से एक को नष्ट करने के बाद, दूसरी वस्तु नष्ट वस्तु को संदर्भित करेगी।

इन कमियों से छुटकारा पाना काफी आसान है:

पट्टा v.2 (स्वचालित)


 class Dog; class Human; class Dog { Human* link; public: Dog(): link(NULL) {} ~Dog() { if (link) link->SetLink(NULL); } void SetLink(Human* l) { if (link == l) return; Human* oldlink = link; link = NULL; if (oldlink) oldlink->SetLink(NULL); link = l; if (link) link->SetLink(this); } const Human* GetLink() const { return link; } } class Human { Dog* link; public: Human(): link(NULL) {} ~Human() { if (link) link->SetLink(NULL); } void SetLink(Dog* l) { if (link == l) return; Dog* oldlink = link; link = NULL; if (oldlink) oldlink->SetLink(NULL); link = l; if (link) link->SetLink(this); } const Dog* GetLink() const { return link; } } 

क्या बदल गया है?

सबसे पहले, SetLink() विधियाँ SetLink() जटिल SetLink() । अब, एक दिशा में एक लिंक सेट करते समय, विधि स्वचालित रूप से दूसरी दिशा में लिंक सेट करती है। लिंक को स्थापित करना भूलना असंभव है। सटीक रूप से गलत बैकलिंक सेट करना असंभव है:
 Human *h1, *h2; h1 = new Human(); h2 = new Human(); Dog *d = new Dog(); h1->SetLink(d); assert(h1->GetLink()->GetLink() == h1); //passed (d    h1) d->SetLink(h2); assert(d->GetLink()->GetLink() == d); //passed (h2    d) assert(h1->GetLink() == NULL); //passed (h1    d) 

दूसरे, विनाशकारी दिखाई दिए कि, जब कोई वस्तु नष्ट हो जाती है, तो स्वचालित रूप से संबंधित वस्तु से लिंक हटा दें, यदि कोई हो। इस प्रकार, GetLink() द्वारा GetLink() आप हमेशा वैध पॉइंटर या NULL की अपेक्षा कर सकते हैं

खैर, अब, ऐसा लगता है, सभी कमियों को समाप्त कर दिया गया है और आप इसका उपयोग कर सकते हैं। हालांकि, एक चौकस पाठक यह नोटिस करेगा कि मानव और डॉग वर्गों का कार्यान्वयन समान है और केवल उपयोग किए गए प्रकारों में भिन्न है। और फिर मैंने सोचा: "मुझे इसे टेम्प्लेट के लिए फिर से करना होगा!" ऐसा कहा जाता है - किया।

पट्टा v.3 (टेम्प्लेट)


एक टेम्प्लेट में एक या अधिक प्रकारों के साथ पैरामीटर शामिल होता है। हमारे मामले में, इनमें से दो प्रकार हैं: एम (मेरा) और एल (लिंक), और टेम्पलेट को क्रमशः ओ <एम, एल> के रूप में लिखा गया है। इस तरह के टेम्पलेट की विशेषज्ञता का उदाहरण पॉइंटर को L के ऑब्जेक्ट की तरह स्टोर करने में सक्षम है, अर्थात यह कनेक्शन M -> L को व्यवस्थित करता है वर्गों और बी के बीच दो-तरफा संचार बनाने के लिए दो सममित विशेषज्ञताओं का उपयोग किया जाता है: ओ <ए, बी> और ओ <बी, ए>

इस प्रकार, टाइप M लिंक के आंतरिक छोर के प्रकार को दिखाता है और इस सूचक को सही ढंग से डालने के लिए आवश्यक है, और L लिंक के बाहरी छोर के प्रकार को दिखाता है:
 template<class M, class L> class O { L* link; public: O(): link(NULL) {} ~O() { if (link) link->SetLink(NULL); } void SetLink(L* l) { if (link == l) return; L* oldlink = link; link = NULL; if (oldlink) oldlink->SetLink(NULL); link = l; if (link) link->SetLink(static_cast<M*>(this)); } const L* GetLink() const { return link; } }; 

इस टेम्पलेट वर्ग का उपयोग करना बहुत सरल है:
 class Human; class Dog; class Human: public O<Human, Dog> {}; class Dog: public O<Dog, Human> {}; Human* h = new Human(); Dog* d = new Dog(); h->SetLink(d); 

बहुत बढ़िया! हमने अपनी कक्षाओं को दो गुना कम दर्ज किया!

द्विदिश लिंक के इस कार्यान्वयन की एक अच्छी विशेषता यह है कि कंपाइलर हमारे लिए कुछ तार्किक त्रुटियां निर्धारित कर सकता है।

उदाहरण के लिए, एक वर्ग की अनुपस्थिति जो लिंक का पारस्परिक है। यही है, अगर कोड में एक वर्ग शामिल है जिसमें लिंक मानव -> कुत्ता हो सकता है , लेकिन कोई वर्ग नहीं है जिसमें लिंक कुत्ता -> मानव शामिल हो सकता है, तो यह कोड संकलित नहीं करेगा:
 template<class M, class L> class O { ... }; class Human; class Dog; class Human: public O<Human, Dog> {}; class Dog {}; 


बेशक, यह एक ही प्रकार की वस्तुओं को बांधने की अनुमति है:
 template<class M, class L> class O { ... }; class Human: public O<Human, Human> {}; Human *h1, *h2; ... h1->SetLink(h2); 


तुम भी अपने आप को एक लिंक सेट कर सकते हैं। यदि यह अस्वीकार्य है, तो इसके SetLink() विधि को ठीक करना आसान है।

अब बहुत अच्छा लग रहा है! लेकिन क्या होगा यदि डॉग ऑब्जेक्ट के संदर्भ के अलावा, कैट ऑब्जेक्ट के संदर्भ में मानव की आवश्यकता है?

पट्टा v.4 (कई लिंक)


यह भी कई विरासत और थोड़ा संशोधित हे वर्ग का उपयोग करके आसानी से व्यवस्थित किया जा सकता है:
 template<class M, class L> class O { L* link; public: O(): link(NULL) {} ~O() { if (link) link->O<L,M>::SetLink(NULL); } void SetLink(L* l) { if (link == l) return; L* oldlink = link; link = NULL; if (oldlink) oldlink->O<L,M>::SetLink(NULL); link = l; if (link) link->O<L,M>::SetLink(static_cast<M*>(this)); } const L* GetLink() const { return link; } }; class Human; class Dog; class Cat; class Human: public O<Human, Dog>, public O<Human, Cat> {}; class Dog: public O<Dog, Human> {}; class Cat: public O<Cat, Human> {}; 

O <L, M> SetLink एक स्पष्ट रिज़ॉल्यूशन SetLink ताकि SetLink फ़ंक्शन को सही बेस क्लास से SetLink । एक चौकस पाठक यह नोटिस करेगा कि कंपाइलर SetLink() सही SetLink() फंक्शन को उस प्रकार के पैरामीटर से चुन सकता है, जैसा कि ओवरलोड कार्यों के साथ होता है। हालाँकि, यदि ये कार्य विभिन्न कक्षाओं में हैं (हमारे मामले में, अलग - अलग मूल वर्गों में), तो C ++ मानक के अनुसार, ओवरलोडिंग काम नहीं करती है [1]।

वही परिवर्तन हमारी कक्षाओं के उपयोगकर्ताओं को प्रभावित करेंगे:
 Human* h; Dog* d; Cat* c; ... // h->SetLink(d); // ,  h->O<Human, Dog>::SetLink(d); //,   h->O<Human, Cat>::SetLink(c); //      //   GetLink() // h->GetLink(); //,  Link   -      h->O<Human, Dog>::GetLink(); //,   

यह मानव वर्ग के थोड़ा शोधन द्वारा सरल बनाया जा सकता है। इसे हमारे स्वयं के तरीकों के टेम्पलेट कार्यान्वयन में जोड़ें SetLink() और GetLink() , जो मूल वर्ग के संबंधित विधि को कॉल करेगा, जो प्रकार पर निर्भर करता है:
 class Human: public O<Human, Dog>, public O<Human, Cat> { public: template<class T> const T* GetLink() const { return O<Human,T>::GetLink(); } template<class T> void SetLink(T* l) { O<Human,T>::SetLink(l); } }; 

अब एकल कनेक्शन के साथ सब कुछ लगभग उतना ही सरल है:
 Human* h; Dog* d; Cat* c; ... h->SetLink<Dog>(d); h->SetLink<Cat>(c); //   ,     h->SetLink(d); h->SetLink(c); h->GetLink<Dog>(); //  Dog h->GetLink<Cat>(); //  Cat 


क्या हुआ था?


यह एक सुविधाजनक (मेरी राय में) टेम्पलेट वर्ग निकला, जो आपको उनकी अखंडता के स्वत: रखरखाव के साथ कड़ाई से परिभाषित प्रकारों की वस्तुओं के बीच द्विदिश संचार स्थापित करने की अनुमति देता है

हां, यह मत पूछिए कि मैंने टेम्प्लेट क्लास को अक्षर O क्यों कहा सबसे अच्छा, पट्टा (पट्टा) नाम उनके अनुरूप होगा।

4.4.3 gcc का उपयोग करके लिखित परीक्षण किया गया था।

संदर्भ


[१] bytes.com/topic/c/answers/137040-multiple-inheritance-ambiguity

पुनश्च। यदि कोई वर्तमान C ++ मानक के ऑनलाइन संस्करण को जानता है, तो लिंक साझा करें - मैं मूल स्रोत को संदर्भित करना चाहता हूं।

पी पी एस। जबकि यह विषय सैंडबॉक्स में धीमी आग पर खत्म हो रहा था, मुझे इस बात का विचार था कि C ++ 0x का उपयोग करते हुए, कई लिंक के मामले में O के उपयोग को कैसे सरल बनाया जाए। देखते रहो।

Source: https://habr.com/ru/post/In126501/


All Articles