الدوال المجردة في السمات
جزء من قسم 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 يمكنها الاعتماد بأمان على وجود طرق معينة، مما يجعل الكود الخاص بك أكثر قابلية للتنبؤ والصيانة.
التحدي
سهللنقم ببناء نظام تسعير حيث تحسب أنواع المنتجات المختلفة أسعارها النهائية بشكل مختلف، لكنها تشترك جميعاً في منطق خصم مشترك من خلال trait يحتوي على دالة مجردة (abstract method).
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
Discountable.php— أنشئ trait يسمىDiscountableيوفر وظيفة الخصم. يجب أن يحتوي الـ trait على:- دالة مجردة
getBasePrice(): floatيجب على الفئات التي تستخدم هذا الـ trait تنفيذها. - دالة
applyDiscount(int $percent)تقوم بحساب وإرجاع السعر بعد تطبيق نسبة الخصم على السعر الأساسي. - دالة
getFinalPrice(int $percent)ترجع سلسلة نصية منسقة:"Final price: $[discounted_price]"مع منزلتين عشريتين.
- دالة مجردة
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() مع نسبة الخصم
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)9السمات (Traits)
مقدمة إلى السماتاستخدام سمات متعددةحل تعارض السماتالدوال المجردة في السماتالسمات مقابل الوراثة