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

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

1. extends — الوراثة

PHP
<?php
// Parent class (base class)
class Animal {
    public function __construct(
        public string $name
    ) {}
    
    public function speak(): string {
        return "{$this->name} makes a sound";
    }
    
    public function sleep(): string {
        return "{$this->name} is sleeping";
    }
}

// Child class inherits from parent
class Dog extends Animal {
    // Override the parent method
    public function speak(): string {
        return "{$this->name}: Woof!";
    }
    
    // Add a new method
    public function fetch(): string {
        return "{$this->name} went to fetch the ball";
    }
}

class Cat extends Animal {
    public function speak(): string {
        return "{$this->name}: Meow~";
    }
}

$dog = new Dog("Buddy");
echo $dog->speak();   // Buddy: Woof! (overridden method)
echo $dog->sleep();   // Buddy is sleeping (inherited method)
echo $dog->fetch();   // Buddy went to fetch the ball (new method)

$cat = new Cat("Luna");
echo $cat->speak();   // Luna: Meow~ (overridden method)
echo $cat->sleep();   // Luna is sleeping (inherited method)
?>
💡 نصيحة: تستخدم لغة PHP الوراثة الأحادية — حيث لا يمكن لأي فئة أن ترث سوى extends فئة أصلية واحدة. أما إمكانيات الوراثة المتعددة، فتتحقق من خلال السمات والواجهات (التي سيتم تناولها في الدروس القادمة).


2. parent — استدعاء طرق الكائن الأصلي

عندما تقوم فئة فرعية بتجاوز طريقة ما، لكنك لا تزال ترغب في الاحتفاظ بالمنطق الأصلي للفئة الأصلية، استخدم parent:::

PHP
<?php
class Vehicle {
    public function __construct(
        public string $brand,
        public int $year
    ) {}
    
    public function getInfo(): string {
        return "{$this->year} {$this->brand}";
    }
}

class ElectricCar extends Vehicle {
    public function __construct(
        string $brand,
        int $year,
        public int $batteryRange  // Extra property for electric cars
    ) {
        // Call the parent constructor
        parent::__construct($brand, $year);
    }
    
    // Override and extend the parent method
    public function getInfo(): string {
        return parent::getInfo() . " Electric (range: {$this->batteryRange}km)";
    }
}

$car = new ElectricCar("Tesla", 2026, 600);
echo $car->getInfo();  // 2026 Tesla Electric (range: 600km)
?>

3. final — منع التوريث/التجاوز

PHP
<?php
class Template {
    // Algorithm skeleton—child classes must not modify it
    final public function render(): string {
        $html = $this->header();
        $html .= $this->body();
        $html .= $this->footer();
        return $html;
    }
    
    protected function header(): string {
        return "<header>Default Header</header>";
    }
    
    protected function body(): string {
        return "<main>Default Content</main>";
    }
    
    protected function footer(): string {
        return "<footer>Default Footer</footer>";
    }
}

// ✅ Child classes can override header/body/footer
class BlogTemplate extends Template {
    protected function header(): string {
        return "<header>Blog Title</header>";
    }
    
    // ❌ But they can't override render()—it's protected by final
    // public function render(): string { ... }
}
?>

4. instanceof — التحقق من النوع

PHP
<?php
function makeSound(Animal $animal): string {
    if ($animal instanceof Dog) {
        return $animal->speak() . " (Good dog!)";
    } elseif ($animal instanceof Cat) {
        return $animal->speak() . " (So cute!)";
    }
    return $animal->speak();
}

echo makeSound(new Dog("Buddy"));  // Buddy: Woof! (Good dog!)
echo makeSound(new Cat("Luna"));   // Luna: Meow~ (So cute!)
?>

5. الفئات المجردة

لا يمكن إنشاء مثيلات للفئات المجردة مباشرةً — فالغرض منها هو تحديد «الشكل الذي يجب أن تكون عليه الفئة الفرعية»:

▶ مثال: التسلسل الهرمي للفئات المجردة للشكل

PHP
<?php
abstract class Shape {
    public function __construct(
        protected string $color = "Black"
    ) {}
    
    // Abstract methods: subclasses must implement these
    abstract public function area(): float;
    abstract public function perimeter(): float;
    
    // Regular method: subclasses can inherit or override
    public function describe(): string {
        return "A {$this->color} shape, area: " . round($this->area(), 2);
    }
}

class Circle extends Shape {
    public function __construct(
        private float $radius,
        string $color = "Black"
    ) {
        parent::__construct($color);
    }
    
    public function area(): float {
        return pi() * pow($this->radius, 2);
    }
    
    public function perimeter(): float {
        return 2 * pi() * $this->radius;
    }
}

class Rectangle extends Shape {
    public function __construct(
        private float $width,
        private float $height,
        string $color = "Black"
    ) {
        parent::__construct($color);
    }
    
    public function area(): float {
        return $this->width * $this->height;
    }
    
    public function perimeter(): float {
        return 2 * ($this->width + $this->height);
    }
}

// $s = new Shape("Red");  // ❌ Abstract classes can't be instantiated
$c = new Circle(5, "Red");
echo $c->describe();  // A Red shape, area: 78.54

$r = new Rectangle(4, 6, "Blue");
echo $r->describe();  // A Blue shape, area: 24
?>
▶ جرّب الكود

6. الواجهات

تحدد الواجهة عقدًا بشأن «ما يمكنك فعله»، دون تحديد «كيفية القيام بذلك»:

PHP
<?php
interface Payable {
    public function getAmount(): float;
    public function pay(): string;
}

interface Refundable {
    public function refund(): string;
}

// A class can implement multiple interfaces
class CreditCard implements Payable, Refundable {
    public function __construct(
        private string $cardNumber,
        private float $amount
    ) {}
    
    public function getAmount(): float {
        return $this->amount;
    }
    
    public function pay(): string {
        return "Credit card {$this->cardNumber} paid {$this->amount}";
    }
    
    public function refund(): string {
        return "Refunded {$this->amount} to card {$this->cardNumber}";
    }
}

class Crypto implements Payable {
    public function __construct(
        private float $amount
    ) {}
    
    public function getAmount(): float {
        return $this->amount;
    }
    
    public function pay(): string {
        return "Crypto payment of {$this->amount}";
    }
    // Crypto doesn't support refunds—so it doesn't implement Refundable
}
?>

7. تعدد الأشكال في الممارسة العملية

الفكرة الأساسية للتعدد الشكلي: التعامل مع كائنات الفئات الفرعية المختلفة من خلال نوع أبوي/واجهة موحد:

▶ مثال: نظام الدفع متعدد الأشكال

PHP
<?php
class PaymentProcessor {
    /**
     * Process payment with polymorphism—works regardless of payment method
     */
    public function process(Payable $payment): void {
        echo "[Payment] {$payment->pay()}<br>";
        
        // Some payment methods support refunds
        if ($payment instanceof Refundable) {
            echo "[Refund Support] {$payment->refund()}<br>";
        }
    }
    
    /**
     * Batch settlement—since all implement Payable, we can handle them uniformly
     */
    public function settle(array $payments): float {
        $total = 0;
        foreach ($payments as $payment) {
            $total += $payment->getAmount();
            echo $payment->pay() . "<br>";
        }
        echo "Total: {$total}<br>";
        return $total;
    }
}

$processor = new PaymentProcessor();

// Polymorphism—the caller doesn't know the specific payment method
$processor->settle([
    new CreditCard("1234-5678", 199.00),
    new Crypto(0.05),
    new CreditCard("8765-4321", 59.99),
]);
?>
▶ جرّب الكود
خاصية فئة مجردة واجهة
يمكن أن تحتوي على خصائص
يمكن أن تحتوي على طرق مُنفَّذة ❌ (في PHP 8 وما بعده يمكن أن تحتوي على طرق افتراضية)
يمكن أن تحتوي على ثوابت
الوراثة المتعددة ❌ (الوراثة الفردية) ✅ (واجهات متعددة)
تُستخدم لـ علاقات «هو نوع من» قدرات «يمكنه القيام بـ»
💡 نصيحة: إرشادات الاختيار: استخدم الفئات المجردة عندما تكون هناك صلة منطقية قوية بين الفئات الفرعية (Shape→Circle). واستخدم الواجهات عندما تحتاج فئات غير مرتبطة ببعضها إلى قدرة مشتركة (CreditCard/Crypto→Payable).

❓ أسئلة شائعة

س متى يجب عليّ استخدام abstract class مقابل interface؟
ج استخدم فئة مجردة عندما تشترك الفئات الفرعية في خصائص أو منطق (على سبيل المثال، تحتوي فئة Shape على خاصية اللون). واستخدم واجهة عندما تحتاج فئات غير مرتبطة ببعضها تمامًا إلى قدرة مشتركة (الدفع، التسجيل، التسلسل).
س لماذا لا يدعم PHP الوراثة المتعددة؟
ج تؤدي الوراثة المتعددة إلى «مشكلة الماس» — فعندما تحتوي فئتان أمّيتان على دالات تحملان الاسم نفسه، لا تعرف الفئة الفرعية أيهما يجب أن تستخدم. ويستبدل PHP الوراثة المتعددة بالواجهات + السمات.
س ما الهدف من الفئات والأساليب final؟
ج لمنع تعديل المنطق الأساسي. استخدم final في الكود الأساسي مثل حسابات الدفع أو المصادقة لضمان عدم تمكن الفئات الفرعية من تجاوزه، مما يضمن اتساق السلوك.

📖 ملخص

📝 تمارين

  1. قم بإنشاء فئة أم Vehicle (العلامة التجارية/السنة/الحصول على المعلومات)، ثم قم بإنشاء الفئتين الفرعيتين Car وMotorcycle. قم بتجاوز getInfo() مع استخدام parent::getInfo() للحفاظ على ناتج الفئة الأم.
  2. أنشئ واجهة Logger (log(string $message): void)، ثم قم بتنفيذ فئتين: FileLogger (تكتب إلى ملف) وDatabaseLogger (تعرض النتائج على الشاشة). استخدم التعدد الشكلي للتعامل مع التسجيل بشكل موحد.
  3. قم بإنشاء فئة مجردة Employee (الاسم/الراتب + طريقة مجردة calculateBonus())، ثم قم بتنفيذ الفئتين الفرعيتين Manager وDeveloper، بحيث يكون لكل منهما منطق حساب المكافأة الخاص بها.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%