التركيب
جزء من قسم Object Oriented Programming في رحلة Rust على Coddy — الدرس 55 من 61.
تعتمد العديد من اللغات كائنية التوجه بشكل كبير على الوراثة لمشاركة الوظائف بين الأنواع. تتخذ لغة رُست (Rust) نهجاً مختلفاً: التركيب (composition). فبدلاً من وراثة السلوك من فئة أب، يمكنك بناء أنواع معقدة من خلال تضمين أنواع أخرى كحقول.
فكر في نمذجة سيارة. بدلاً من إنشاء فئة Vehicle أساسية والوراثة منها، فإنك تقوم بتكوين Car من مكونات أصغر وأكثر تركيزاً:
struct Engine {
horsepower: u32,
}
struct Wheels {
count: u8,
diameter: f32,
}
struct Car {
engine: Engine,
wheels: Wheels,
brand: String,
}
لا يرث Car من Engine أو Wheels—بل هو يمتلكها. علاقة "has-a" هذه هي التركيب (composition). يمكنك الوصول إلى المكونات الداخلية من خلال حقولها (fields) واستدعاء دوالها (methods) مباشرة:
impl Engine {
fn start(&self) {
println!("Engine with {} HP started!", self.horsepower);
}
}
impl Car {
fn start(&self) {
self.engine.start(); // تفويض إلى المكون الداخلي
}
}
يحافظ هذا النمط على تركيز كل struct على مسؤولية واحدة. هل تحتاج إلى تغيير كيفية عمل المحركات؟ قم بتعديل الـ Engine struct فقط. هل تريد إعادة استخدام Wheels لدراجة نارية؟ ما عليك سوى تضمينها في struct جديد. يمنحك التركيب (Composition) المرونة دون الارتباط الوثيق (tight coupling) الذي غالباً ما يخلقه التوريث (inheritance).
التحدي
سهللنقم ببناء نظام حاسوب باستخدام التركيب (composition)! بدلاً من إنشاء هيكل واحد ضخم، ستقوم بتركيب Computer من مكونات أصغر وأكثر تركيزاً—وهي CPU و Memory. سيعرف كل مكون كيفية وصف نفسه، وسيقوم الـ Computer بتفويض المهام إلى أجزائه.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
cpu.rs: قم بتعريف هيكلCPUعام (public) مع حقلين عامين:cores(u32) لعدد الأنوية، وspeed_ghz(f32) لسرعة الساعة. أضف ميثودspecsتعيد سلسلة نصية (String) تصف المعالج بالتنسيق"{cores}-core @ {speed_ghz}GHz".memory.rs: قم بتعريف هيكلMemoryعام مع حقل عامsize_gb(u32) لحجم الذاكرة. أضف ميثودspecsتعيد سلسلة نصية بالتنسيق"{size_gb}GB RAM".main.rs: قم باستيراد كلا الموديولين وأنشئ هيكلComputerعام يحتوي على حقلcpuمن نوعCPUوحقلmemoryمن نوعMemory. قم بتنفيذ ميثودsystem_infoعلى الـ Computer تقوم بالتفويض لمكوناته وتطبع مواصفات النظام الكاملة. استخدم المدخلات المقدمة لبناء Computer وعرض معلوماته.
يجب أن تقوم ميثود system_info بطباعة:
System: {cpu_specs} | {memory_specs}على سبيل المثال، مع المدخلات 8 و 3.5 و 16:
System: 8-core @ 3.5GHz | 16GB RAMومع المدخلات 4 و 2.8 و 32:
System: 4-core @ 2.8GHz | 32GB RAMستتلقى ثلاثة مدخلات: أنوية المعالج (قم بتحويلها إلى u32)، سرعة المعالج (قم بتحويلها إلى f32)، وحجم الذاكرة (قم بتحويلها إلى u32).
ورقة مرجعية
تستخدم لغة Rust التركيب (composition) بدلاً من الوراثة لمشاركة الوظائف بين الأنواع. يمكنك بناء أنواع معقدة من خلال تضمين أنواع أخرى كحقول، مما يؤدي إلى إنشاء علاقة "يحتوي على" (has-a).
التركيب الأساسي
قم بتعريف هياكل (structs) أصغر وأكثر تركيزاً وقم بتركيبها في هياكل أكبر:
struct Engine {
horsepower: u32,
}
struct Wheels {
count: u8,
diameter: f32,
}
struct Car {
engine: Engine,
wheels: Wheels,
brand: String,
}
التفويض للمكونات
يمكنك الوصول إلى المكونات الداخلية من خلال حقولها واستدعاء دوالها (methods):
impl Engine {
fn start(&self) {
println!("Engine with {} HP started!", self.horsepower);
}
}
impl Car {
fn start(&self) {
self.engine.start(); // التفويض للمكون الداخلي
}
}
يحافظ التركيب على تركيز كل هيكل (struct) على مسؤولية واحدة، مما يوفر المرونة دون اقتران وثيق.
جرّب بنفسك
mod cpu;
mod memory;
use cpu::CPU;
use memory::Memory;
// TODO: قم بتعريف هيكل Computer عام مع حقلين عامين:
// - cpu: CPU
// - memory: Memory
// TODO: قم بتنفيذ طريقة system_info على Computer بحيث:
// - تقوم بالتفويض إلى cpu.specs() و memory.specs()
// - تطبع: System: {cpu_specs} | {memory_specs}
fn main() {
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let cores: u32 = input.trim().parse().expect("Invalid number");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let speed_ghz: f32 = input.trim().parse().expect("Invalid number");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let size_gb: u32 = input.trim().parse().expect("Invalid number");
// TODO: قم بإنشاء مثيلات CPU و Memory باستخدام المدخلات التي تم تحليلها
// TODO: قم بإنشاء مثيل Computer مع CPU و Memory
// TODO: قم باستدعاء system_info() على الكمبيوتر لطباعة النتيجة
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1الأساليب والسلوك
مقدمة في كتل التنفيذمعامل Selfالأساليب القابلة للتعديلالدوال المرتبطةكتل تنفيذ متعددةتسلسل الأساليبمراجعة - إجراءات المستطيل4مشروع: حيوان أليف افتراضي
تعريف الحيوان الأليفإطعام الحيوان الأليف2التغليف والوحدات البرمجية
أساسيات الوحدات البرمجيةالكلمة المفتاحية Publicالحقول الخاصةدوال Gettersدوال Settersمراجعة - الخزانة الآمنة5الأنواع العامة (Generics)
الـ Structs العامةالـ Methods العامةأنواع عامة متعددةالـ Functions العامةمراجعة - Coordinate Point3التعدادات المتقدمة (Advanced Enums)
التعدادات مع البياناتالأساليب (Methods) في التعداداتمطابقة حالات البياناتمراجعة التعداد Optionملخص - التعداد Shape6تعريف الـ Traits
ما هي الـ Trait؟تنفيذ الـ Traitsالتنفيذات الافتراضيةتجاوز التنفيذات الافتراضيةالـ Traits مع المعاملاتملخص - مشغل الوسائط