أنماط التصميم في Rust: ليست مجرد موضة، بل ضرورة
عندما نتحدث عن Rust، يتخيل الكثيرون نظام الأنواع، مدقق الاقتراض (borrow checker) وغياب جامع القمامة. لكن خلف هذه الواجهة الصارمة تختبئ أداة قوية لتصميم البنية. أنماط التصميم (design patterns) في Rust ليست مجرد تقليد لتقاليد Java أو C++. إنها طريقة لجعل المترجم يعمل لصالحك، وليس ضدك.
في هذه المقالة، سنشرح لماذا تبدو الأنماط الكلاسيكية من GoF (Gang of Four) في Rust مختلفة، وأي منها مفيد حقًا، وأيها يعتبر أنماطًا مضادة. والأهم من ذلك، سنرى كودًا حقيقيًا يمكن تطبيقه فورًا في مشروعك.
لماذا يغير Rust قواعد اللعبة؟
غالبًا ما تحل أنماط التصميم الكلاسيكية مشكلات تتعلق بإدارة الذاكرة، الوراثة، وتعدد الأشكال. بينما يقدم Rust نهجًا مختلفًا تمامًا:
- الملكية والاقتراض — بدلاً من جمع القمامة، تدير الذاكرة بشكل صريح. الأنماط التي تحل في لغات أخرى مشكلة "من يملك الكائن" تصبح في Rust تافهة أو غير ضرورية تمامًا.
- السمات (Traits) — بديل للوراثة. لا يدعم Rust الوراثة الكلاسيكية للبرمجة كائنية التوجه، لكن السمات توفر مرونة دون مشاكل الوراثة المعينية.
- التعدادات (Enums) ومطابقة الأنماط — أداة قوية تحل محل العديد من الأنماط المتعلقة بالحالة والتفرع.
- الأسلوب الوظيفي — المكررات، الإغلاقات، والدمج تسمح بكتابة كود موجز دون أنماط صريحة.
لذلك، تبدو العديد من الأنماط في Rust ليس كـ "فئة وواجهة"، بل كـ سمة + هيكل + تعداد. دعنا نلقي نظرة على أمثلة محددة.
نمط Builder: بناء الكائنات المعقدة
نمط Builder هو واحد من أشهر الأنماط في Rust. إنه مثالي لإنشاء كائنات تحتوي على عدد كبير من المعاملات، خاصة عندما يكون بعضها اختياريًا.
المشكلة: مُنشئ بـ 10 معاملات
تخيل أنك بحاجة لإنشاء تكوين لعميل HTTP. في النهج التقليدي، ستكتب:
let client = HttpClient::new( "https://api.example.com", Duration::from_secs(30), 5, true, None, vec!["Accept: application/json".to_string()], ...);هذا غير قابل للقراءة ومن السهل الخطأ في ترتيب الوسائط. يحل Builder هذه المشكلة.
التنفيذ في Rust
في Rust، يتم تنفيذ نمط Builder من خلال هيكل منفصل مع دوال ضبط (setters) تعيد self:
#[derive(Debug)]pub struct HttpClient { base_url: String, timeout: Duration, max_retries: u32, verify_ssl: bool, headers: Vec<String>,}
pub struct HttpClientBuilder { base_url: String, timeout: Duration, max_retries: u32, verify_ssl: bool, headers: Vec<String>,}
impl HttpClientBuilder { pub fn new(base_url: &str) -> Self { HttpClientBuilder { base_url: base_url.to_string(), timeout: Duration::from_secs(30), max_retries: 3, verify_ssl: true, headers: Vec::new(), } }
pub fn timeout(mut self, duration: Duration) -> Self { self.timeout = duration; self }
pub fn max_retries(mut self, n: u32) -> Self { self.max_retries = n; self }
pub fn no_ssl(mut self) -> Self { self.verify_ssl = false; self }
pub fn add_header(mut self, header: &str) -> Self { self.headers.push(header.to_string()); self }
pub fn build(self) -> Result<HttpClient, String> { if self.base_url.is_empty() { return Err("base_url cannot be empty".to_string()); } Ok(HttpClient { base_url: self.base_url,