Menu
CoddyTech

التركيب مقابل الوراثة (Composition vs Inheritance)

جزء من قسم البرمجة كائنية التوجه في رحلة Dart على Coddy. الدرس 82 من 110.

لقد تعلمت أن الوراثة تنشئ علاقة «هو نوع من»؛ فـ Dog هو Animal. لكن هناك نهجًا قويًا آخر: التركيب، الذي ينشئ علاقة «يمتلك». فبدلًا من وراثة السلوك، يحتوي الصنف على مثيلات من أصناف أخرى ويفوّض إليها المهام.

لنفترض وجود فئة Car. باستخدام الوراثة، قد تحاول توسيع فئة Engine، لكن السيارة ليست محركًا، بل لديها محرك:

class Engine {
  void start() => print('Engine started');
}

class Car {
  final Engine _engine = Engine();  // التركيب: لدى Car كائن من Engine
  
  void start() {
    _engine.start();  // فوّض المهمة إلى المحرك
    print('Car is ready to drive');
  }
}

void main() {
  var car = Car();
  car.start();
}

الميزة الأساسية للتركيب هي المرونة. مع الوراثة، تكون مقيدًا بفئة أب واحدة. أما مع التركيب، فيمكنك الجمع بين مكونات متعددة وحتى تبديلها أثناء التشغيل:

class ElectricEngine {
  void start() => print('Electric motor humming');
}

class HybridCar {
  Engine? _gasEngine;
  ElectricEngine? _electricEngine;
  
  HybridCar({bool useElectric = false}) {
    if (useElectric) {
      _electricEngine = ElectricEngine();
    } else {
      _gasEngine = Engine();
    }
  }
}

من الإرشادات الشائعة "فضّل التركيب على الوراثة". استخدم الوراثة عندما تكون هناك علاقة حقيقية من نوع «هو نوع من» وتحتاج إلى تعدد الأشكال. استخدم التركيب عندما تريد إعادة استخدام السلوك دون اقتران وثيق، أو عندما يحتاج كائن إلى إمكانات من مصادر متعددة.

challenge icon

التحدي

سهل

لنبنِ نظام حاسوب باستخدام التركيب! بدلًا من محاولة جعل الحاسوب يرث من أجزائه، ستنشئ فئات منفصلة للمكوّنات وتجمعها معًا — لأن الحاسوب لديه وحدة معالجة مركزية ولديه ذاكرة، وليس وحدة معالجة مركزية أو ذاكرة.

ستنظّم الكود في ثلاثة ملفات:

  • components.dart: أنشئ فئتَين للمكوّنات تمثّلان أجزاء الحاسوب:
    • فئة CPU تحتوي على String brand وdouble speedGHz. أضف إليها طريقة process() تُعيد سلسلة نصية: [brand] processing at [speedGHz]GHz
    • فئة Memory تحتوي على int sizeGB. أضف إليها طريقة load() تُعيد سلسلة نصية: Loading [sizeGB]GB of data
  • computer.dart: أنشئ فئة Computer تستخدم التركيب لجمع المكوّنات. ينبغي أن يقوم حاسوبك بما يلي:
    • يحتوي على حقل String name
    • يضم نسخة من CPU ونسخة من Memory (تركيب — الحاسوب لديه هذه الأجزاء)
    • يقبل جميع القيم اللازمة عبر مُنشئه لإنشاء كلا المكوّنين داخليًا
    • يحتوي على طريقة boot() تفوّض العمل إلى كلا المكوّنين وتطبع ثلاثة أسطر: اسم الحاسوب متبوعًا بـ starting...، ثم نتيجة طريقة المعالجة في وحدة المعالجة المركزية، ثم نتيجة طريقة التحميل في الذاكرة
    • يحتوي على طريقة specs() تُعيد سلسلة نصية تصف النظام: [name]: [brand] [speedGHz]GHz, [sizeGB]GB RAM
  • main.dart: استورد ملفاتك واعرض التركيب عمليًا:
    • أنشئ Computer باسم Workstation مع وحدة معالجة مركزية من نوع Intel تعمل بسرعة 3.5 غيغاهرتز وذاكرة سعتها 16 غيغابايت
    • استدعِ الطريقة boot()
    • اطبع نتيجة specs()

لاحظ كيف أن فئة Computer لا ترث من CPU أو Memory — بل تحتوي عليهما وتفوّض إليهما العمل. هذه هي علاقة «يحتوي على» التي ينشئها التركيب!

الناتج المتوقع:

Workstation starting...
Intel processing at 3.5GHz
Loading 16GB of data
Workstation: Intel 3.5GHz, 16GB RAM

جرّب بنفسك

import 'computer.dart';

void main() {
  // TODO: أنشئ Computer باسم "Workstation" بالمواصفات التالية:
  // - معالج Intel يعمل بتردد 3.5 GHz
  // - ذاكرة بسعة 16 GB

  // TODO: استدعِ الدالة boot()

  // TODO: اطبع نتيجة specs()
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Dart عبر الإنترنت