أنماط التصميم في Rust مع أمثلة
أنماط التصميم هي حلول مثبتة للمشكلات النمطية في تطوير البرمجيات. في Rust، نظرًا لنظامها الفريد في الملكية والاقتراض وأوقات الحياة، فإن تنفيذ أنماط GoF الكلاسيكية غالبًا ما يتطلب نهجًا خاصًا. في هذه المقالة، سنشرح العديد من الأنماط الشائعة مع أمثلة من كود Rust حقيقي.
لماذا يغير Rust منهجية الأنماط؟
على عكس اللغات التي تستخدم جمع القمامة (Java, C#)، يفرض Rust قواعد صارمة على ملكية الذاكرة. وهذا يجعل بعض التطبيقات الكلاسيكية (مثل Singleton من خلال حالة قابلة للتغيير عالمية) غير آمنة أو غير فعالة. ومع ذلك، يقدم Rust بدائله الأنيقة القائمة على الأنواع والـ traits.
1. Builder — إنشاء الكائنات خطوة بخطوة
Builder هو أحد أكثر الأنماط استخدامًا في Rust. إنه مثالي لتكوين الهياكل المعقدة، خاصة عندما يكون هناك العديد من الحقول الاختيارية.
مثال: إنشاء طلب HTTP
struct Request { method: String, url: String, headers: Vec<(String, String)>, body: Option<String>,}
struct RequestBuilder { method: String, url: String, headers: Vec<(String, String)>, body: Option<String>,}
impl RequestBuilder { fn new(method: &str, url: &str) -> Self { Self { method: method.to_string(), url: url.to_string(), headers: Vec::new(), body: None, } }
fn header(mut self, key: &str, value: &str) -> Self { self.headers.push((key.to_string(), value.to_string())); self }
fn body(mut self, body: &str) -> Self { self.body = Some(body.to_string()); self }
fn build(self) -> Request { Request { method: self.method, url: self.url, headers: self.headers, body: self.body, } }}
fn main() { let req = RequestBuilder::new("POST", "https://api.example.com/data") .header("Content-Type", "application/json") .header("Authorization", "Bearer token123") .body(r#"{"key": "value"}"#) .build();
println!("URL: {}", req.url); println!("Headers count: {}", req.headers.len());}مزايا النهج في Rust
- الأمان: يتم إنشاء الكائن فقط بعد استدعاء
build()— التكوين غير المكتمل غير ممكن. - قابلية القراءة: سلسلة الأساليب بديهية وسهلة الفهم.
- أمان الأنواع: يتحقق المترجم من الأنواع في كل خطوة.
2. Strategy — اختيار الخوارزمية أثناء التشغيل
Strategy يسمح بتغيير سلوك الكائن أثناء التنفيذ. في Rust، يتم تنفيذ ذلك من خلال الـ traits والإرسال الديناميكي (Box مع traits) أو الإرسال الثابت (generics).
مثال: فرز البيانات
trait SortStrategy { fn sort(&self, data: &mut [i32]);}
struct BubbleSort;struct QuickSort;
impl SortStrategy for BubbleSort { fn sort(&self, data: &mut [i32]) { // أبسط تطبيق لفرز الفقاعات for i in 0..data.len() { for j in 0..data.len() - 1 - i { if data[j] > data[j + 1] { data.swap(j, j + 1); } } } }}
impl SortStrategy for QuickSort { fn sort(&self, data: &mut [i32]) { // الفرز السريع (مبسط) if data.len() > 1 { let pivot = data.len() / 2; data.sort(); // نستخدم الدالة المدمجة كمثال } }}
struct Sorter { strategy: Box<dyn SortStrategy>,}
impl Sorter { fn new(strategy: Box<dyn SortStrategy>) ->