Rust में आर्किटेक्चर क्यों विलासिता नहीं, बल्कि आवश्यकता है
\n\nजब आप Rust में कोड लिखते हैं, तो टाइप सिस्टम और borrow checker पहले से ही आपको 80% त्रुटियों से बचाते हैं। लेकिन मेमोरी सुरक्षा केवल नींव है। यदि आप इस पर अव्यवस्थित आर्किटेक्चर बनाते हैं, तो देर-सबेर प्रोजेक्ट आपसी निर्भरताओं की भूलभुलैया में बदल जाएगा, जहाँ एक मॉड्यूल में बदलाव दस अन्य को तोड़ देता है।
\n\nRust में आर्किटेक्चरल पैटर्न फैशन का मामला या OOP दुनिया का अवशेष नहीं हैं। ये सामान्य समस्याओं के सिद्ध समाधान हैं: ऐसी वस्तु कैसे बनाएं जो इनवेरिएंट का उल्लंघन न करे? घटकों को कैसे जोड़ें ताकि उनका अलग-अलग परीक्षण किया जा सके? तर्क को कैसे व्यवस्थित करें ताकि मौजूदा कोड को बदले बिना उसका विस्तार किया जा सके?
\n\nइस लेख में, हम Rust पर वास्तविक उदाहरणों के साथ तीन समूहों (क्रिएशनल, स्ट्रक्चरल, बिहेवियरल) के पैटर्न पर चर्चा करेंगे। कोई अमूर्त सिद्धांत नहीं - केवल कोड जिसे आप तुरंत लागू कर सकते हैं।
\n\nक्रिएशनल पैटर्न: वस्तुओं को सही ढंग से बनाना
\n\nBuilder - 10 पैरामीटर वाले कंस्ट्रक्टर से मुक्तिदाता
\n\nRust में कंस्ट्रक्टर ओवरलोडिंग नहीं है। यदि किसी स्ट्रक्चर में 3-4 से अधिक फ़ील्ड हैं, तो सभी मामलों के लिए एक कंस्ट्रक्टर लिखने का मतलब है उपयोगकर्ता को Option और Default के साथ लंबी सिग्नेचर पढ़ने के लिए मजबूर करना। Builder इस समस्या को सुरुचिपूर्ण ढंग से हल करता है।
struct ServerConfig {\n host: String,\n port: u16,\n workers: u8,\n enable_tls: bool,\n max_connections: u32,\n}\n\nstruct ServerConfigBuilder {\n host: Option<String>,\n port: Option<u16>,\n workers: Option<u8>,\n enable_tls: Option<bool>,\n max_connections: Option<u32>,\n}\n\nimpl ServerConfigBuilder {\n fn new() -> Self {\n Self {\n host: None,\n port: None,\n workers: None,\n enable_tls: None,\n max_connections: None,\n }\n }\n\n fn host(mut self, host: &str) -> Self {\n self.host = Some(host.to_string());\n self\n }\n\n fn port(mut self, port: u16) -> Self {\n self.port = Some(port);\n self\n }\n\n fn workers(mut self, workers: u8) -> Self {\n self.workers = Some(workers);\n self\n }\n\n fn enable_tls(mut self, enable: bool) -> Self {\n self.enable_tls = Some(enable);\n self\n }\n\n fn max_connections(mut self, max: u32) -> Self {\n self.max_connections = Some(max);\n self\n }\n\n fn build(self) -> Result<ServerConfig, String> {\n Ok(ServerConfig {\n host: self.host.unwrap_or_else(|| "127.0.0.1".to_string()),\n port: self.port.unwrap_or(8080),\n workers: self.workers.unwrap_or(4),\n enable_tls: self.enable_tls.unwrap_or(false),\n max_connections: self.max_connections.unwrap_or(100),\n })\n }\n}\n\n// उपयोग\nlet config = ServerConfigBuilder::new()\n .host("0.0.0.0")\n .port(443)\n .enable_tls(true)\n .build()\n .expect("अमान्य कॉन्फ़िग");\n\nयह Rust में क्यों काम करता है? Builder पैटर्न स्वामित्व प्रणाली के साथ पूरी तरह से मेल खाता है: विधियों की श्रृंखला self का उपभोग करती है और लौटाती है, और अंतिम build या तो तैयार वस्तु लौटाता है या सत्यापन त्रुटि देता है। कोई म्यूटेबल रेफरेंस और डेटा रेस नहीं।
फ़ैक्टरी - जब रिटर्न प्रकार पैरामीटर पर निर्भर करता है
\n\nकल्पना करें कि आपको विभिन्न प्रारूपों को पार्स करने की आवश्यकता है: JSON, YAML, TOML। हर जगह match का उपयोग करने के बजाय फ़ैक्टरी विधि का उपयोग करें।
trait Parser {\n fn parse(&self, input: &str) -> Result<serde_json::Value, String>;\n}\n\nstruct JsonParser;\nimpl Parser for JsonParser {\n fn parse(&self, input: &str) -> Result<serde_json::Value, String> {\n serde_json::from_str(input).map_err(|e| e.to_string())\n }\n}\n\nstruct YamlParser;\nimpl Parser for YamlParser {\n fn parse(&self, input: &str) -> Result<serde_json::