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

نمط المزين

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

إن نمط Decorator هو نمط تصميم هيكلي يتيح لك إضافة سلوكيات جديدة إلى الكائنات ديناميكيًا من خلال تغليفها في كائنات تغليف خاصة تُسمى decorators. وعلى عكس الوراثة، التي تضيف السلوك وقت الترجمة، تضيف decorators السلوك وقت التشغيل.

تخيّل متجرًا لـ coffee. تبدأ بـ coffee أساسي، ثم تضيف إضافات مثل الحليب أو السكر أو الكريمة المخفوقة.

كل إضافة تُغلّف القهوة الأصلية وتضيف إلى وصفها وتكلفتها. وتتمثل الفكرة الأساسية في أن كلًا من القهوة الأساسية وdecorators يشتركان في الواجهة نفسها.

<?php
interface Coffee {
    public function getDescription(): string;
    public function getCost(): float;
}

class SimpleCoffee implements Coffee {
    public function getDescription(): string {
        return "Simple Coffee";
    }
    
    public function getCost(): float {
        return 2.00;
    }
}

تُطبّق Decorators الواجهة نفسها وتغلّف كائن Coffee آخر:

<?php
class MilkDecorator implements Coffee {
    public function __construct(private Coffee $coffee) {}
    
    public function getDescription(): string {
        return $this->coffee->getDescription() . ", Milk";
    }
    
    public function getCost(): float {
        return $this->coffee->getCost() + 0.50;
    }
}

class SugarDecorator implements Coffee {
    public function __construct(private Coffee $coffee) {}
    
    public function getDescription(): string {
        return $this->coffee->getDescription() . ", Sugar";
    }
    
    public function getCost(): float {
        return $this->coffee->getCost() + 0.25;
    }
}

$coffee = new SimpleCoffee();
$coffee = new MilkDecorator($coffee);
$coffee = new SugarDecorator($coffee);

echo $coffee->getDescription() . " = $" . $coffee->getCost();

Output:

Simple Coffee, Milk, Sugar = $2.75

يلتف كل decorator حول الكائن السابق، مُشكِّلًا سلسلة. يتيح لك هذا النمط دمج السلوكيات بأي ترتيب وبأي عدد دون إنشاء فئة لكل تركيبة ممكنة. هل تحتاج إلى كمية مضاعفة من Milk؟ ما عليك سوى تغليفه مرتين.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا لطلب البيتزا باستخدام نمط Decorator. ستبدأ ببيتزا أساسية وتضيف الإضافات ديناميكيًا: كل إضافة تغلّف البيتزا وadds إلى description وprice الخاصين بها، تمامًا كما في مثال coffee من الدرس.

ستنظّم التعليمات البرمجية عبر أربعة ملفات:

  • Pizza.php: Define واجهة Pizza التي تحدد العقد لجميع أنواع البيتزا والإضافات. يجب أن تحتوي على طريقتين: getDescription(): string وgetPrice(): float.
  • BasePizza.php: تضمين واجهة Pizza وإنشاء فئة MargheritaPizza التي implements إياها. هذه هي البيتزا الأساسية، مع description بقيمة "Margherita" وprice بقيمة 8.00.
  • Toppings.php: تضمين واجهة Pizza وإنشاء ثلاث فئات decorator التي implements إياها. كل decorator يغلّف كائن Pizza (يتم Accept الكائن في constructor) ويعزّز كلًا من description وprice:
    • CheeseTopping: يضيف ", Extra Cheese" إلى description و1.50 إلى price
    • PepperoniTopping: يضيف ", Pepperoni" إلى description و2.00 إلى price
    • MushroomTopping: يضيف ", Mushrooms" إلى description و1.25 إلى price
  • main.php: تضمين الملفين BasePizza وToppings. ستتلقى input واحدًا: قائمة إضافات مفصولة بفواصل لإضافتها (مثلًا "cheese,pepperoni" أو "mushroom,cheese,cheese").

    ابدأ باستخدام MargheritaPizza. ولكل إضافة في input، غلّف البيتزا باستخدام decorator المناسب:

    • "cheese" تُغلّف باستخدام CheeseTopping
    • "pepperoni" تُغلّف باستخدام PepperoniTopping
    • "mushroom" تُغلّف باستخدام MushroomTopping

    بعد تطبيق جميع الإضافات، اطبع description وprice النهائيين للبيتزا بهذا التنسيق:

    [description] = $[price]

    نسّق price ليحتوي على منزلتين عشريتين.

لاحظ كيف يمكن تطبيق الإضافة نفسها عدة مرات: طلب جبنة مضاعفة يعني ببساطة التغليف باستخدام CheeseTopping مرتين. هذه المرونة هي قوة نمط Decorator!

جرّب بنفسك

<?php
require_once 'BasePizza.php';
require_once 'Toppings.php';

// اقرأ المدخلات - قائمة إضافات مفصولة بفواصل
$input = trim(fgets(STDIN));
$toppings = explode(',', $input);

// ابدأ بقاعدة MargheritaPizza
$pizza = new MargheritaPizza();

// TODO: مر عبر كل إضافة وغلّف البيتزا بالمزخرف المناسب
// - "cheese" -> غلّف بـ CheeseTopping
// - "pepperoni" -> غلّف بـ PepperoniTopping
// - "mushroom" -> غلّف بـ MushroomTopping

// TODO: اطبع النتيجة بالتنسيق: [description] = $[price]
// نسّق السعر إلى منزلتين عشريتين باستخدام number_format()

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

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

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

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