C++ में व्यवहार में डिपेंडेंसी इंजेक्शन: उदाहरण और टेम्पलेट

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

परिचय: C++ में डिपेंडेंसी इंजेक्शन क्यों महत्वपूर्ण है?

\n\n

आधुनिक C++ विकास में, कक्षाओं के बीच ढीली युग्मन (loose coupling) का समर्थन कोड के लचीलेपन और परीक्षण क्षमता की कुंजी है। डिपेंडेंसी इंजेक्शन (DI) या निर्भरता इंजेक्शन एक पैटर्न है जिसमें ऑब्जेक्ट अपनी निर्भरताएँ बाहर से प्राप्त करता है, न कि स्वयं बनाता है। यह सीधे SOLID से डिपेंडेंसी इनवर्ज़न सिद्धांत (Dependency Inversion Principle) को लागू करता है।

\n\n

DI के बिना, कक्षाएँ अक्सर विशिष्ट कार्यान्वयन से कसकर बंधी होती हैं:

\n\n
// बुरा: क्लास स्वयं निर्भरता बनाता है\nclass ReportGenerator {\n    Database db_; // विशिष्ट DB से कसकर बंधा\npublic:\n    ReportGenerator() : db_("localhost") {}\n    void generate() { db_.query("SELECT ..."); }\n};
\n\n

DI के साथ, हमें लचीलापन, कार्यान्वयन को बदलने की क्षमता (उदाहरण के लिए, यूनिट परीक्षणों के लिए) मिलती है, और कोड रखरखाव सरल हो जाता है। इस लेख में, हम C++ में DI लागू करने के तीन व्यावहारिक तरीकों पर चर्चा करेंगे: कंस्ट्रक्टर के माध्यम से, इंटरफ़ेस के माध्यम से, और एक सरल कंटेनर की सहायता से।

\n\n

1. कंस्ट्रक्टर के माध्यम से इंजेक्शन (Constructor Injection)

\n\n

सबसे सामान्य और अनुशंसित तरीका। निर्भरताएँ क्लास के कंस्ट्रक्टर में पैरामीटर के रूप में पारित की जाती हैं। C++ में, इसके लिए स्मार्ट पॉइंटर (std::unique_ptr, std::shared_ptr) या संदर्भों का उपयोग करना सुविधाजनक है।

\n\n

उदाहरण: लॉगर और सेवा

\n\n
#include <iostream>\n#include <memory>\n#include <string>\n\n// 1. एब्स्ट्रैक्शन (इंटरफ़ेस)\nclass ILogger {\npublic:\n    virtual ~ILogger() = default;\n    virtual void log(const std::string& msg) = 0;\n};\n\n// 2. विशिष्ट कार्यान्वयन\nclass ConsoleLogger : public ILogger {\npublic:\n    void log(const std::string& msg) override {\n        std::cout << "[LOG]: " << msg << std::endl;\n    }\n};\n\n// 3. क्लास जो ILogger पर निर्भर है\nclass UserService {\n    std::shared_ptr<ILogger> logger_; // निर्भरता\npublic:\n    // कंस्ट्रक्टर के माध्यम से इंजेक्शन\n    explicit UserService(std::shared_ptr<ILogger> logger)\n        : logger_(std::move(logger)) {}\n\n    void createUser(const std::string& name) {\n        // व्यावसायिक तर्क...\n        logger_->log("User created: " + name);\n    }\n};\n\nint main() {\n    auto logger = std::make_shared<ConsoleLogger>();\n    UserService service(logger);\n    service.createUser("Alice");\n    return 0;\n}
\n\n

लाभ:

\n
    \n
  • निर्भरता कंस्ट्रक्टर के हस्ताक्षर में स्पष्ट रूप से दिखाई देती है।
  • \n
  • ऑब्जेक्ट हमेशा मान्य स्थिति में होता है (बनने के बाद)।
  • \n
  • परीक्षणों के लिए कार्यान्वयन को बदलना आसान है (उदाहरण के लिए, MockLogger)।
  • \n
\n\n

नुकसान: बड़ी संख्या में निर्भरताओं के साथ, कंस्ट्रक्टर भारी हो सकता है। ऐसे मामलों में, कंटेनर मदद करते हैं (अनुभाग 3 देखें)।

\n\n

2. इंटरफ़ेस और पॉलीमॉर्फ़िज़्म के माध्यम से इंजेक्शन

\n\n

यह पिछले दृष्टिकोण का विस्तार है। हम सभी निर्भरताओं के लिए शुद्ध वर्चुअल इंटरफ़ेस परिभाषित करते हैं। क्लाइंट कोड केवल एब्स्ट्रैक्शन के साथ काम करता है, और विशिष्ट कार्यान्वयन ऑब्जेक्ट निर्माण चरण (composition root) पर प्रतिस्थापित किए जाते हैं।

\n\n

उदाहरण: रिपॉज़िटरी और व्यावसायिक तर्क

\n\n
#include <vector>\n#include <memory>\n\n// रिपॉज़िटरी का इंटरफ़ेस\nclass IUserRepository {\npublic:\n    virtual ~IUserRepository() = default;\n    virtual void add(const std::string& user) = 0;\n    virtual std::vector<std::string> getAll() const = 0;\n};\n\n// डेटाबेस के लिए कार्यान्वयन\nclass DatabaseUserRepo : public IUserRepository {\npublic:\n    void add(const std::string& user) override {\n        // SQL क्वेरी\n    }\n    std::vector<std::string> getAll() const override {\n        return {"Alice", "Bob"};\n    }\n};\n\n// व्यावसायिक तर्क, DB के बारे में नहीं जानता\nclass UserManager {\n    std::uniq                

ब्लॉग

पुस्तक अनुशंसाएँ