أساسيات PHP

الوراثة والتعددية الشكلية

13 دقيقة الدرس 23 من 45

فهم الوراثة

تسمح لك الوراثة بإنشاء صنف جديد بناءً على صنف موجود. يرث الصنف الجديد (الابن) جميع الخصائص والدوال من الصنف الموجود (الأب)، ويمكنه إضافة أو تجاوز الوظائف.

المصطلحات الأساسية:
  • الصنف الأب (الأساس/الفائق): الصنف الذي يتم الوراثة منه
  • الصنف الابن (المشتق/الفرعي): الصنف الذي يرث
  • extends: الكلمة المفتاحية المستخدمة لإنشاء الوراثة
  • الوراثة تعزز إعادة استخدام الكود وتنشئ علاقات بين الأصناف

مثال وراثة أساسي

لننشئ تسلسل وراثة بسيط:

<?php // الصنف الأب class Animal { protected $name; protected $age; protected $species; public function __construct($name, $age, $species) { $this->name = $name; $this->age = $age; $this->species = $species; } public function eat() { return "{$this->name} يأكل."; } public function sleep() { return "{$this->name} ينام."; } public function getInfo() { return "{$this->name} هو {$this->species} عمره {$this->age} سنوات"; } } // الصنف الابن يرث من Animal class Dog extends Animal { private $breed; public function __construct($name, $age, $breed) { // استدعاء بناء الأب parent::__construct($name, $age, "كلب"); $this->breed = $breed; } // إضافة دالة جديدة خاصة بـ Dog public function bark() { return "{$this->name} يقول: عواء! عواء!"; } // إضافة دالة أخرى public function fetch() { return "{$this->name} يحضر الكرة!"; } } // صنف ابن آخر class Cat extends Animal { private $color; public function __construct($name, $age, $color) { parent::__construct($name, $age, "قطة"); $this->color = $color; } public function meow() { return "{$this->name} يقول: مواء!"; } public function scratch() { return "{$this->name} يخدش الأثاث!"; } } // الاستخدام $dog = new Dog("ماكس", 3, "لابرادور"); echo $dog->bark(); // دالة خاصة بالكلب echo $dog->eat(); // موروثة من Animal echo $dog->sleep(); // موروثة من Animal echo $dog->getInfo(); // موروثة من Animal $cat = new Cat("ويسكرز", 2, "رمادي"); echo $cat->meow(); // دالة خاصة بالقطة echo $cat->eat(); // موروثة من Animal ?>
parent::__construct(): استدعِ دائماً بناء الأب لتهيئة الخصائص الموروثة بشكل صحيح. استخدم parent:: للوصول إلى دوال الصنف الأب.

تجاوز الدوال

يمكن للأصناف الأبناء تجاوز دوال الأب لتوفير سلوك متخصص:

<?php class Animal { protected $name; public function __construct($name) { $this->name = $name; } public function makeSound() { return "{$this->name} يصدر صوتاً"; } public function move() { return "{$this->name} يتحرك"; } } class Dog extends Animal { // تجاوز دالة makeSound public function makeSound() { return "{$this->name} ينبح: عواء! عواء!"; } // تجاوز دالة move public function move() { return "{$this->name} يجري على أربع أرجل"; } } class Bird extends Animal { // تجاوز دالة makeSound public function makeSound() { return "{$this->name} يزقزق: تغريد! تغريد!"; } // تجاوز دالة move public function move() { return "{$this->name} يطير في السماء"; } } class Fish extends Animal { // تجاوز دالة makeSound public function makeSound() { return "{$this->name} يصنع فقاعات"; } // تجاوز دالة move public function move() { return "{$this->name} يسبح تحت الماء"; } } // كل حيوان يتصرف بشكل مختلف $dog = new Dog("ماكس"); echo $dog->makeSound(); // ماكس ينبح: عواء! عواء! echo $dog->move(); // ماكس يجري على أربع أرجل $bird = new Bird("تويتي"); echo $bird->makeSound(); // تويتي يزقزق: تغريد! تغريد! echo $bird->move(); // تويتي يطير في السماء $fish = new Fish("نيمو"); echo $fish->makeSound(); // نيمو يصنع فقاعات echo $fish->move(); // نيمو يسبح تحت الماء ?>

فهم التعددية الشكلية

التعددية الشكلية تعني "أشكال متعددة." تسمح للكائنات من أصناف مختلفة بأن تُعامل ككائنات من صنف أب مشترك، مع الحفاظ على سلوكياتها الفريدة.

<?php // التعددية الشكلية في العمل function describeAnimal(Animal $animal) { echo $animal->makeSound() . "\n"; echo $animal->move() . "\n"; } $dog = new Dog("بادي"); $bird = new Bird("تشيربي"); $fish = new Fish("جولدي"); // نفس الدالة تتعامل مع أنواع حيوانات مختلفة describeAnimal($dog); // سلوك الكلب describeAnimal($bird); // سلوك الطائر describeAnimal($fish); // سلوك السمكة ?>
فوائد التعددية الشكلية:
  • مرونة الكود: كتابة دوال تعمل مع أي صنف فرعي
  • قابلية التوسع: إضافة أصناف جديدة دون تغيير الكود الموجود
  • قابلية الصيانة: أسهل في إدارة وتحديث الكود

مثال واقعي: نظام الموظفين

لنبني نظام إدارة موظفين عملي:

<?php class Employee { protected $name; protected $id; protected $baseSalary; protected $department; public function __construct($name, $id, $baseSalary, $department) { $this->name = $name; $this->id = $id; $this->baseSalary = $baseSalary; $this->department = $department; } public function getName() { return $this->name; } public function getId() { return $this->id; } // يمكن تجاوزها من قبل الأصناف الأبناء public function calculateSalary() { return $this->baseSalary; } public function getDetails() { return "المعرف: {$this->id}, الاسم: {$this->name}, القسم: {$this->department}"; } // يمكن تجاوزها لأنواع موظفين مختلفة public function getRole() { return "موظف"; } } class Manager extends Employee { private $bonus; private $teamSize; public function __construct($name, $id, $baseSalary, $department, $teamSize) { parent::__construct($name, $id, $baseSalary, $department); $this->teamSize = $teamSize; $this->bonus = 0; } public function setBonus($amount) { $this->bonus = $amount; } // تجاوز حساب الراتب public function calculateSalary() { return $this->baseSalary + $this->bonus; } // تجاوز الدور public function getRole() { return "مدير (فريق من {$this->teamSize})"; } public function conductMeeting() { return "{$this->name} يعقد اجتماع فريق"; } } class Developer extends Employee { private $programmingLanguages; private $projectBonus; public function __construct($name, $id, $baseSalary, $department, $languages) { parent::__construct($name, $id, $baseSalary, $department); $this->programmingLanguages = $languages; $this->projectBonus = 0; } public function addProjectBonus($amount) { $this->projectBonus += $amount; } // تجاوز حساب الراتب public function calculateSalary() { return $this->baseSalary + $this->projectBonus; } // تجاوز الدور public function getRole() { return "مطور (" . implode(", ", $this->programmingLanguages) . ")"; } public function writeCode() { return "{$this->name} يكتب كود بـ " . implode(", ", $this->programmingLanguages); } } class Intern extends Employee { private $mentor; private $duration; // أشهر public function __construct($name, $id, $baseSalary, $department, $mentor, $duration) { parent::__construct($name, $id, $baseSalary, $department); $this->mentor = $mentor; $this->duration = $duration; } // تجاوز حساب الراتب (المتدربون قد يحصلون على راتب مخفض) public function calculateSalary() { return $this->baseSalary * 0.6; // 60% من الراتب الأساسي } // تجاوز الدور public function getRole() { return "متدرب (برنامج {$this->duration} أشهر)"; } public function learn() { return "{$this->name} يتعلم من المرشد: {$this->mentor}"; } } // دالة تستخدم التعددية الشكلية function printEmployeeReport(Employee $employee) { echo "=== تقرير الموظف ===\n"; echo $employee->getDetails() . "\n"; echo "الدور: " . $employee->getRole() . "\n"; echo "الراتب: $" . number_format($employee->calculateSalary(), 2) . "\n"; echo "===================\n\n"; } // إنشاء موظفين مختلفين $manager = new Manager("أحمد علي", "M001", 8000, "الهندسة", 10); $manager->setBonus(2000); $dev = new Developer("سارة حسن", "D001", 6000, "الهندسة", ["PHP", "JavaScript", "Python"]); $dev->addProjectBonus(1000); $intern = new Intern("عمر خليل", "I001", 2000, "الهندسة", "سارة حسن", 6); // استخدام التعددية الشكلية - نفس الدالة لجميع أنواع الموظفين printEmployeeReport($manager); printEmployeeReport($dev); printEmployeeReport($intern); // دوال خاصة بالنوع echo $manager->conductMeeting() . "\n"; echo $dev->writeCode() . "\n"; echo $intern->learn() . "\n"; ?>

الكلمة المفتاحية final

استخدم final لمنع الأصناف من الوراثة أو الدوال من التجاوز:

<?php // لا يمكن الوراثة منه final class Configuration { private $settings; public function get($key) { return $this->settings[$key] ?? null; } } // هذا سيسبب خطأ: // class CustomConfig extends Configuration {} // خطأ! class User { protected $name; // لا يمكن تجاوزها في الأصناف الأبناء final public function getId() { return $this->id; } // يمكن تجاوزها public function getRole() { return "مستخدم"; } } class Admin extends User { // هذا جيد public function getRole() { return "مسؤول"; } // هذا سيسبب خطأ: // public function getId() {} // خطأ: لا يمكن تجاوز دالة final } ?>
متى تستخدم final: استخدمها للدوال الحرجة التي يجب ألا تُغير (الأمان، الترخيص، الوظائف الأساسية) أو للأصناف التي لا يجب الوراثة منها.

الأصناف المجردة

تعمل الأصناف المجردة كقوالب ولا يمكن إنشاء مثيل منها مباشرة:

<?php abstract class Shape { protected $color; public function __construct($color) { $this->color = $color; } public function getColor() { return $this->color; } // دالة مجردة - يجب تنفيذها من قبل الأصناف الأبناء abstract public function calculateArea(); abstract public function calculatePerimeter(); // دالة ملموسة - يمكن استخدامها كما هي public function describe() { return "شكل {$this->color} بمساحة " . $this->calculateArea(); } } class Rectangle extends Shape { private $width; private $height; public function __construct($color, $width, $height) { parent::__construct($color); $this->width = $width; $this->height = $height; } // يجب تنفيذ الدوال المجردة public function calculateArea() { return $this->width * $this->height; } public function calculatePerimeter() { return 2 * ($this->width + $this->height); } } class Circle extends Shape { private $radius; public function __construct($color, $radius) { parent::__construct($color); $this->radius = $radius; } public function calculateArea() { return pi() * $this->radius ** 2; } public function calculatePerimeter() { return 2 * pi() * $this->radius; } } // لا يمكن فعل هذا: // $shape = new Shape("أحمر"); // خطأ: لا يمكن إنشاء مثيل من صنف مجرد // يمكن فعل هذا: $rect = new Rectangle("أزرق", 10, 5); echo $rect->describe(); // شكل أزرق بمساحة 50 echo $rect->calculatePerimeter(); // 30 $circle = new Circle("أحمر", 7); echo $circle->describe(); // شكل أحمر بمساحة 153.94 echo $circle->calculatePerimeter(); // 43.98 ?>
الأصناف المجردة مقابل الأصناف العادية:
  • مجرد: لا يمكن إنشاء مثيل منه، يمكن أن يحتوي على دوال مجردة
  • عادي: يمكن إنشاء مثيل منه، يجب تنفيذ جميع الدوال
  • استخدم مجرد: عندما تريد تعريف واجهة مشتركة لكن إجبار الأصناف الأبناء على تنفيذ دوال محددة

الوراثة متعددة المستويات

يمكن للأصناف أن ترث من أصناف ترث من أصناف أخرى:

<?php class Vehicle { protected $brand; public function __construct($brand) { $this->brand = $brand; } public function start() { return "بدء مركبة {$this->brand}..."; } } class Car extends Vehicle { protected $doors; public function __construct($brand, $doors) { parent::__construct($brand); $this->doors = $doors; } public function honk() { return "بيب بيب!"; } } class ElectricCar extends Car { private $batteryCapacity; public function __construct($brand, $doors, $batteryCapacity) { parent::__construct($brand, $doors); $this->batteryCapacity = $batteryCapacity; } public function charge() { return "شحن {$this->brand} ببطارية {$this->batteryCapacity}kWh"; } // يمكن الوصول إلى دوال من جميع الأجداد public function displayInfo() { return $this->start() . " " . $this->honk() . " " . $this->charge(); } } $tesla = new ElectricCar("تسلا", 4, 100); echo $tesla->start(); // من Vehicle echo $tesla->honk(); // من Car echo $tesla->charge(); // من ElectricCar echo $tesla->displayInfo(); // يستخدم الثلاثة ?>
تمرين:

أنشئ نظام معالجة مدفوعات مع الوراثة:

  • صنف مجرد Payment مع خصائص: amount, currency, status
  • دوال مجردة: processPayment(), validatePayment()
  • دالة ملموسة: getDetails()
  • صنف CreditCardPayment يرث من Payment مع: cardNumber, expiryDate, cvv
  • صنف PayPalPayment يرث من Payment مع: email, transactionId
  • صنف BankTransferPayment يرث من Payment مع: accountNumber, bankName
  • نفذ جميع الدوال المجردة بمنطق مناسب
  • أنشئ دالة processPayments(array $payments) تستخدم التعددية الشكلية

أفضل الممارسات

إرشادات مهمة:
  • فضل التكوين على الوراثة: أحياناً من الأفضل أن تحتوي الكائنات على كائنات أخرى بدلاً من الوراثة
  • احتفظ بالوراثة سطحية: تجنب سلاسل الوراثة العميقة (3+ مستويات)
  • استخدم protected للبيانات المشتركة: اسمح للأصناف الأبناء بالوصول لما يحتاجونه
  • استدعِ بناءات الأب: استدعِ دائماً parent::__construct() عند التجاوز
  • وثق الوراثة: اجعل من الواضح أي دوال يجب/لا يجب تجاوزها

الملخص

في هذا الدرس، تعلمت:

  • كيف تنشئ الوراثة علاقات أب-ابن بين الأصناف
  • كيفية استخدام الكلمة المفتاحية extends
  • تجاوز الدوال لتخصيص السلوك
  • التعددية الشكلية وفوائدها
  • الكلمة المفتاحية final لمنع الوراثة/التجاوز
  • الأصناف المجردة كقوالب للأصناف الأبناء
  • تسلسلات الوراثة متعددة المستويات
  • أفضل الممارسات للوراثة الفعالة

بعد ذلك، سنتعلم عن الأعضاء الثابتة والثوابت للبيانات والوظائف على مستوى الصنف!