Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الدوال المجردة في السمات

جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 54 من 91.

يمكن للسمات (Traits) توفير دوال جاهزة للاستخدام، ولكن في بعض الأحيان تحتاج إلى الفئة التي تستخدم السمة لتنفيذ وظائف محددة. هنا تبرز فائدة الدوال المجردة في السمات (abstract methods in traits) - فهي تحدد عقداً يجب على الفئة المستخدمة الوفاء به.

عندما تعلن سمة (trait) عن طريقة مجردة (abstract method)، يجب على أي فئة (class) تستخدم تلك السمة تنفيذها:

<?php
trait Notifiable {
    abstract public function getEmail(): string;
    
    public function sendNotification(string $message): void {
        echo "Sending to " . $this->getEmail() . ": $message";
    }
}

class User {
    use Notifiable;
    
    public function __construct(private string $email) {}
    
    public function getEmail(): string {
        return $this->email;
    }
}

$user = new User("john@example.com");
$user->sendNotification("Welcome!");

الناتج:

Sending to john@example.com: Welcome!

توفر الـ trait الأسلوب sendNotification() ولكنها تعتمد على getEmail() ليتم تنفيذها بواسطة الـ class. يخلق هذا نمطاً قوياً حيث تتعامل الـ trait مع المنطق المشترك بينما تفوض التفاصيل الخاصة بالـ class إلى الـ class المنفذة.

إذا استخدمت فئة (class) الـ trait دون تنفيذ الطريقة المجردة (abstract method)، فسيقوم PHP بإصدار خطأ فادح (fatal error). يضمن ذلك أن الـ traits يمكنها الاعتماد بأمان على وجود طرق معينة، مما يجعل الكود الخاص بك أكثر قابلية للتنبؤ والصيانة.

challenge icon

التحدي

سهل

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

ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:

  • Discountable.php — أنشئ trait يسمى Discountable يوفر وظيفة الخصم. يجب أن يحتوي الـ trait على:
    • دالة مجردة getBasePrice(): float يجب على الفئات التي تستخدم هذا الـ trait تنفيذها.
    • دالة applyDiscount(int $percent) تقوم بحساب وإرجاع السعر بعد تطبيق نسبة الخصم على السعر الأساسي.
    • دالة getFinalPrice(int $percent) ترجع سلسلة نصية منسقة: "Final price: $[discounted_price]" مع منزلتين عشريتين.
    يتولى الـ trait جميع العمليات الحسابية للخصم، ولكنه يعتمد على كل فئة لتخبره ما هو السعر الأساسي.
  • Product.php — أنشئ فئة Product تستخدم الـ trait المسمى Discountable. قم بتضمين ملف الـ trait. استخدم خاصية constructor promotion لتعريف $name (string) خاص و $price (float) خاص. قم بتنفيذ دالة getBasePrice() المطلوبة لإرجاع سعر المنتج.
  • main.php — قم بتضمين ملف Product. ستتلقى ثلاثة مدخلات: اسم المنتج، السعر، ونسبة الخصم. أنشئ كائناً (instance) من Product واطبع سطرين:
    • السعر الأساسي منسقاً كـ "Base: $[price]" مع منزلتين عشريتين.
    • نتيجة استدعاء getFinalPrice() مع نسبة الخصم.

هذا النمط قوي لأن الـ trait المسمى Discountable يمكن استخدامه من قبل أي فئة — منتجات، خدمات، اشتراكات — طالما أنها تقوم بتنفيذ getBasePrice(). يوفر الـ trait منطق الخصم المشترك بينما تحدد كل فئة من أين يأتي سعرها الأساسي.

ورقة مرجعية

يمكن للسمات (Traits) التصريح عن أساليب مجردة (abstract methods) يجب تنفيذها بواسطة أي فئة تستخدم هذه السمة:

<?php
trait Notifiable {
    abstract public function getEmail(): string;
    
    public function sendNotification(string $message): void {
        echo "Sending to " . $this->getEmail() . ": $message";
    }
}

class User {
    use Notifiable;
    
    public function __construct(private string $email) {}
    
    public function getEmail(): string {
        return $this->email;
    }
}

توفر السمة أساليب جاهزة للاستخدام مع مطالبة الفئة بتنفيذ وظائف محددة. إذا استخدمت فئة ما السمة دون تنفيذ الأسلوب المجرد، فسيقوم PHP بإصدار خطأ فادح (fatal error).

يسمح هذا النمط للسمات بمعالجة المنطق المشترك مع تفويض التفاصيل الخاصة بالفئة إلى الفئة المنفذة.

جرّب بنفسك

<?php

require_once 'Product.php';

// قراءة المدخلات
$name = trim(fgets(STDIN));
$price = floatval(trim(fgets(STDIN)));
$discountPercent = intval(trim(fgets(STDIN)));

// TODO: إنشاء كائن (instance) من Product بالاسم والسعر

// TODO: طباعة السعر الأساسي بتنسيق "Base: $[price]" مع منزلتين عشريتين

// TODO: طباعة نتيجة استدعاء getFinalPrice() مع نسبة الخصم

?>
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming