Rust में डिपेंडेंसी इंजेक्शन: व्यवहार में

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

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

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

Rust में डिपेंडेंसी इंजेक्शन: व्यवहार में

\n\n

डिपेंडेंसी इंजेक्शन (DI) एक डिज़ाइन पैटर्न है जो ऑब्जेक्ट को बाहर से निर्भरताएँ पास करने की अनुमति देता है, न कि उन्हें अंदर बनाने की। Rust में यह दृष्टिकोण स्वामित्व प्रणाली, ट्रेट्स और जेनेरिक्स के कारण विशेष रूप से उपयोगी है। इस लेख में, हम सरल उदाहरणों से लेकर डिपेंडेंसी कंटेनरों के साथ उन्नत तकनीकों तक, Rust में DI को व्यवहार में लागू करने का तरीका देखेंगे।

\n\n

Rust में DI क्यों महत्वपूर्ण है?

\n

Rust में, जहाँ विरासत के साथ क्लासिकल OOP नहीं है, DI मदद करता है:

\n
    \n
  • युग्मन कम करना — मॉड्यूल ठोस कार्यान्वयन के बजाय अमूर्तताओं पर निर्भर करते हैं।
  • \n
  • परीक्षण को सरल बनाना — आप वास्तविक कार्यान्वयन को मॉक से बदल सकते हैं।
  • \n
  • लचीलापन बढ़ाना — कोड बदले बिना सिस्टम के व्यवहार को आसानी से बदल सकते हैं।
  • \n
\n\n

मूल बातें: पैरामीटर के माध्यम से निर्भरताएँ पास करना

\n

निर्भरता इंजेक्ट करने का सबसे सरल तरीका इसे फ़ंक्शन या स्ट्रक्चर कंस्ट्रक्टर के पैरामीटर के रूप में पास करना है।

\n\n
// अमूर्तता के लिए ट्रेट परिभाषित करना\ntrait Logger {\n    fn log(&self, message: &str);\n}\n\n// कंसोल के लिए कार्यान्वयन\nstruct ConsoleLogger;\n\nimpl Logger for ConsoleLogger {\n    fn log(&self, message: &str) {\n        println!("LOG: {}", message);\n    }\n}\n\n// फ़ाइल के लिए कार्यान्वयन\nstruct FileLogger {\n    filename: String,\n}\n\nimpl Logger for FileLogger {\n    fn log(&self, message: &str) {\n        // वास्तविक कोड में यहाँ फ़ाइल में लिखना होगा\n        println!("FILE: {} -> {}", self.filename, message);\n    }\n}\n\n// सेवा जो कंस्ट्रक्टर के माध्यम से DI का उपयोग करती है\nstruct UserService<L: Logger> {\n    logger: L,\n}\n\nimpl<L: Logger> UserService<L> {\n    fn new(logger: L) -> Self {\n        Self { logger }\n    }\n\n    fn create_user(&self, name: &str) {\n        self.logger.log(&format!("User created: {}", name));\n    }\n}\n\nfn main() {\n    let console_logger = ConsoleLogger;\n    let user_service = UserService::new(console_logger);\n    user_service.create_user("Alice");\n}\n
\n\n

इस उदाहरण में, UserService को नहीं पता कि कौन सा लॉगर उपयोग किया जा रहा है — यह केवल Logger ट्रेट पर निर्भर करता है। यही DI का सार है।

\n\n

उन्नत दृष्टिकोण: ट्रेट-ऑब्जेक्ट और डायनामिक डिस्पैच

\n

जब आपको एक कंटेनर में विभिन्न कार्यान्वयनों को संग्रहीत करने की आवश्यकता हो, तो ट्रेट-ऑब्जेक्ट (Box<dyn Trait>) का उपयोग करें।

\n\n
trait Database {\n    fn query(&self, sql: &str) -> Vec<String>;\n}\n\nstruct PostgresDb;\nimpl Database for PostgresDb {\n    fn query(&self, sql: &str) -> Vec<String> {\n        vec!["result1".to_string(), "result2".to_string()]\n    }\n}\n\nstruct SqliteDb;\nimpl Database for SqliteDb {\n    fn query(&self, sql: &str) -> Vec<String> {\n        vec!["sqlite_result".to_string()]\n    }\n}\n\nstruct App {\n    db: Box<dyn Database>,\n    logger: Box<dyn Logger>,\n}\n\nimpl App {\n    fn new(db: Box<dyn Database>, logger: Box<dyn Logger>) -> Self {\n        Self { db, logger }\n    }\n\n    fn run(&self) {\n        let results = self.db.query("SELECT * FROM users");\n        for r in results {\n            self.logger.log(&r);\n        }\n    }\n}\n\nfn main() {\n    let app = App::new(\n        Box::new(PostgresDb),\n        Box::new(ConsoleLogger),\n    );\n    app.run();\n}\n
\n\n

ट्रेट-ऑब्जेक्ट का लाभ यह है कि आप रनटाइम पर कार्यान्वयन बदल सकते हैं। नुकसान डायनामिक डिस्पैच पर थोड़ा ओवरहेड है।

\n\n

डिपेंडेंसी कंटेनर: इनवर्जन ऑफ कंट्रोल

\n

बड़े प्रोजेक्ट्स के लिए डिपेंडेंसी कंटेनर का उपयोग करना सुविधाजनक है। हालाँकि Rust में कोई मानक DI-कंटेनर नहीं है, इसे स्वयं लागू करना या shaku या dice जैसे क्रेट्स का उपयोग करना आसान है।

\n\n

मैन्युअल रूप से एक सरल कंटेनर का उदाहरण:

\n\n
use std::collections::HashMap;\nuse std::any::Any;\n\n// टाइ                

ब्लॉग

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