لماذا العمارة في Rust ليست ترفاً بل ضرورة
عندما تكتب بلغة Rust، فإن نظام الأنواع ومدقق الاقتراض (borrow checker) يمنحك بالفعل 80% من الحماية ضد الأخطاء. لكن أمان الذاكرة هو مجرد أساس. إذا بنيت عليه بنية فوضوية، عاجلاً أم آجلاً سيتحول المشروع إلى متاهة من التبعيات المتبادلة، حيث يؤدي تغيير وحدة واحدة إلى كسر عشر وحدات أخرى.
أنماط العمارة في Rust ليست موضة أو إرثاً من عالم البرمجة كائنية التوجه (OOP). إنها حلول مجربة للمشكلات النمطية: كيف تنشئ كائناً لا ينتهك الثوابت؟ كيف تربط المكونات بحيث يمكن اختبارها بشكل منفصل؟ كيف تنظم المنطق بحيث يمكن توسيعه دون تعديل الكود الموجود؟
في هذه المقالة، سنستعرض ثلاث مجموعات من الأنماط (الإنشائية والهيكلية والسلوكية) مع أمثلة حقيقية بلغة Rust. لا نظرية مجردة - فقط كود يمكن تطبيقه فوراً.
أنماط الإنشاء: إنشاء الكائنات بشكل صحيح
Builder - المنقذ من المنشئات ذات 10 معاملات
في Rust لا يوجد تحميل زائد للمنشئات. إذا كان للهيكل أكثر من 3-4 حقول، فإن كتابة منشئ واحد لجميع الحالات يعني إجبار المستخدم على قراءة توقيع طويل مع Option و Default. يحل نمط Builder هذه المشكلة بأناقة.
struct ServerConfig { host: String, port: u16, workers: u8, enable_tls: bool, max_connections: u32,}
struct ServerConfigBuilder { host: Option<String>, port: Option<u16>, workers: Option<u8>, enable_tls: Option<bool>, max_connections: Option<u32>,}
impl ServerConfigBuilder { fn new() -> Self { Self { host: None, port: None, workers: None, enable_tls: None, max_connections: None, } }
fn host(mut self, host: &str) -> Self { self.host = Some(host.to_string()); self }
fn port(mut self, port: u16) -> Self { self.port = Some(port); self }
fn workers(mut self, workers: u8) -> Self { self.workers = Some(workers); self }
fn enable_tls(mut self, enable: bool) -> Self { self.enable_tls = Some(enable); self }
fn max_connections(mut self, max: u32) -> Self { self.max_connections = Some(max); self }
fn build(self) -> Result<ServerConfig, String> { Ok(ServerConfig { host: self.host.unwrap_or_else(|| "127.0.0.1".to_string()), port: self.port.unwrap_or(8080), workers: self.workers.unwrap_or(4), enable_tls: self.enable_tls.unwrap_or(false), max_connections: self.max_connections.unwrap_or(100), }) }}
// الاستخدامlet config = ServerConfigBuilder::new() .host("0.0.0.0") .port(443) .enable_tls(true) .build() .expect("إعدادات غير صالحة");لماذا يعمل هذا في Rust؟ يتناسب نمط Builder بشكل مثالي مع نظام الملكية: سلسلة الدوال تستهلك وتعيد self، بينما الدالة النهائية build إما تعيد الكائن الجاهز أو خطأ في التحقق. لا مراجع قابلة للتغيير ولا سباقات بيانات.
المصنع (Factory) - عندما يعتمد نوع الإرجاع على المعامل
تخيل أنك بحاجة لتحليل تنسيقات مختلفة: JSON, YAML, TOML. بدلاً من استخدام match في كل مكان، نستخدم طريقة المصنع.
trait Parser { fn parse(&self, input: &str) -> Result<serde_json::Value, String>;}
struct JsonParser;impl Parser for JsonParser { fn parse(&self, input: &str) -> Result<serde_json::Value, String> { serde_json::from_str(input).map_err(|e| e.to_string()) }}
struct YamlParser;impl Parser for YamlParser { fn parse(&self, input: &str) -> Result<serde_json::