C++ में डिज़ाइन पैटर्न: कब और क्यों
\n\nक्या आपने कभी कोड को दसवीं बार फिर से लिखा है क्योंकि नई कार्यक्षमता पुराने आर्किटेक्चर को तोड़ रही थी? या यह समझने की कोशिश में डिबगिंग में घंटों बिताए कि किसने और क्यों एक ग्लोबल वेरिएबल बदल दिया? परिचित लगता है? तो डिज़ाइन पैटर्न की दुनिया में आपका स्वागत है।
\n\nपैटर्न कोई जादुई गोली नहीं हैं। ये सामान्य समस्याओं के युद्ध-परीक्षित समाधान हैं। वे आपके कोड को परफेक्ट नहीं बनाएंगे, लेकिन वे अन्य डेवलपर्स के साथ एक ही भाषा में बात करने और उन गलतियों से बचने में मदद करेंगे, जिन पर आपसे पहले हजारों प्रोग्रामर पहले ही कदम रख चुके हैं।
\n\nइस लेख में, हम C++ में पैटर्न के तीन मुख्य समूहों पर चर्चा करेंगे: क्रिएशनल, स्ट्रक्चरल और बिहेवियरल। जानेंगे कि उन्हें कब लागू करना चाहिए, और कब उनसे दूर रहना बेहतर है। चलिए शुरू करते हैं!
\n\nक्रिएशनल पैटर्न: ऑब्जेक्ट निर्माण पर नियंत्रण
\n\nक्रिएशनल पैटर्न ऑब्जेक्ट के लचीले निर्माण के लिए जिम्मेदार होते हैं। वे इंस्टेंटिएशन के तर्क को छिपाते हैं और सिस्टम को इस बात से स्वतंत्र बनाते हैं कि इसके घटक कैसे बनाए जाते हैं।
\n\nSingleton (सिंगलटन)
\n\nक्लासिक। यह गारंटी देता है कि एक क्लास का केवल एक ही इंस्टेंस है, और उस तक वैश्विक पहुँच प्रदान करता है। यह आकर्षक लगता है, लेकिन इसे समझदारी से उपयोग करें — यदि यह 'गॉड-ऑब्जेक्ट' के आकार तक फैल जाता है तो Singleton अक्सर एक एंटी-पैटर्न बन जाता है।
\n\nकब उपयोग करें: लॉगर, कॉन्फ़िगरेशन मैनेजर, थ्रेड पूल, हार्डवेयर के साथ काम करना।
\n\nC++ में उदाहरण (thread-safe, C++11 और नए):
\n\nclass Logger {\npublic:\n static Logger& getInstance() {\n static Logger instance; // मल्टीथ्रेडिंग में भी एक बार इनिशियलाइज़ होता है\n return instance;\n }\n\n void log(const std::string& msg) {\n std::cout << "[LOG]: " << msg << std::endl;\n }\n\n // कॉपी और असाइनमेंट को प्रतिबंधित करें\n Logger(const Logger&) = delete;\n Logger& operator=(const Logger&) = delete;\n\nprivate:\n Logger() = default; // प्राइवेट कंस्ट्रक्टर\n ~Logger() = default;\n};\n\n// उपयोग\nLogger::getInstance().log("सिस्टम शुरू हुआ");\n\nFactory Method (फैक्ट्री मेथड)
\n\nयह ऑब्जेक्ट बनाने के लिए एक इंटरफ़ेस परिभाषित करता है, लेकिन सबक्लास को बनाए जाने वाले ऑब्जेक्ट के प्रकार को बदलने की अनुमति देता है। यह तब बहुत उपयुक्त है जब आप पहले से नहीं जानते कि किस क्लास की आवश्यकता होगी।
\n\nकब उपयोग करें: विभिन्न फॉर्मेट (JSON, XML, YAML) को पार्स करना, विभिन्न OS के लिए UI तत्व बनाना, विभिन्न डेटाबेस के साथ काम करना।
\n\nउदाहरण:
\n\n// एब्सट्रैक्ट प्रोडक्ट\nclass Document {\npublic:\n virtual void open() = 0;\n virtual ~Document() = default;\n};\n\n// कंक्रीट प्रोडक्ट\nclass PDFDocument : public Document {\n void open() override { std::cout << "PDF खोल रहे हैं" << std::endl; }\n};\n\nclass WordDocument : public Document {\n void open() override { std::cout << "Word खोल रहे हैं" << std::endl; }\n};\n\n// फैक्ट्री\nclass Application {\npublic:\n virtual Document* createDocument() = 0; // फैक्ट्री मेथड\n virtual ~Application() = default;\n\n void newDocument() {\n Document* doc = createDocument();\n doc->open();\n delete doc; // वास्तविक कोड में — स्मार्ट पॉइंटर\n }\n};\n\nclass PDFApp : public Application {\n Document* createDocument() override { return new PDFDocument(); }\n};\n\n// उपयोग\nPDFApp app;\napp.newDocument(); // PDF खोल रहे हैं\n\nस्ट्रक्चरल पैटर्न: कंस्ट्रक्टर को जोड़ना
\n\nये पैटर्न क्लासेस और ऑब्जेक्ट को बड़ी संरचनाओं में व्यवस्थित करने में मदद करते हैं, लचीलापन और दक्षता बनाए रखते हुए।
\n\nAdapter (एडेप्टर)
\n\nअसंगत इंटरफ़ेस वाले ऑब्जेक्ट को एक साथ काम करने की अनुमति देता है। कल्पना करें कि आपके पास XML के साथ काम करने के लिए एक पुरानी लाइब्रेरी है, और एक नया मॉड्यूल JSON की उम्मीद करता है। एडेप्टर आपका उद्धारकर्ता है।
\n\nकब उपयोग करें: