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

نمط المحول

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

إن نمط المحول (Adapter Pattern) هو نمط تصميم هيكلي يسمح للكائنات ذات الواجهات غير المتوافقة بالعمل معاً. فهو يعمل كجسر بين واجهتين، حيث يقوم بتحويل الاستدعاءات من تنسيق إلى آخر.

تخيل أنك تقوم ببناء تطبيق يعالج المدفوعات، وتحتاج إلى دمج مكتبة دفع تابعة لجهة خارجية. تعمل المكتبة بشكل مثالي، لكن واجهتها لا تتطابق مع ما يتوقعه الكود الخاص بك. بدلاً من إعادة كتابة تطبيقك بالكامل، يمكنك إنشاء محول (adapter) يترجم بين الاثنين.

<?php
// الواجهة التي يتوقعها تطبيقك
interface PaymentProcessor {
    public function pay(float $amount): string;
}

// مكتبة خارجية بواجهة مختلفة
class StripeAPI {
    public function createCharge(int $cents, string $currency): string {
        return "Charged {$cents} {$currency} via Stripe";
    }
}

يقوم المحول (adapter) بتنفيذ الواجهة المتوقعة بينما يستخدم داخلياً فئة الطرف الثالث:

<?php
class StripeAdapter implements PaymentProcessor {
    public function __construct(private StripeAPI $stripe) {}
    
    public function pay(float $amount): string {
        $cents = (int)($amount * 100);
        return $this->stripe->createCharge($cents, 'USD');
    }
}

$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);

المخرجات:

Charged 2999 USD via Stripe

يقوم المحول بتحويل الدولارات إلى سنتات ويستدعي الطريقة المناسبة. يعرف كود تطبيقك فقط عن PaymentProcessor، ويظل غير مدرك تماماً لمتطلبات Stripe المحددة. إذا قمت لاحقاً بالتبديل إلى مزود دفع مختلف، فستقوم ببساطة بإنشاء محول جديد دون تغيير الكود الحالي الخاص بك.

challenge icon

التحدي

سهل

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

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

  • TemperatureSensor.php — قم بتعريف واجهة TemperatureSensor التي يتوقعها تطبيق الطقس الخاص بك. يجب أن تحتوي على طريقة getTemperature(): float تعيد درجة الحرارة بالسيليزيوس.
  • LegacySensor.php — قم بإنشاء فئة LegacySensor تحاكي الأجهزة القديمة. تحتوي هذه الفئة على واجهة مختلفة عما يحتاجه تطبيقك:
    • تأخذ قراءة الفهرنهايت في منشئها (استخدم constructor promotion)
    • تحتوي على طريقة readFahrenheit(): float تعيد قيمة الفهرنهايت المخزنة
  • LegacySensorAdapter.php — قم بتضمين كل من واجهة TemperatureSensor وفئة LegacySensor. أنشئ فئة LegacySensorAdapter التي تنفذ TemperatureSensor وتغلف LegacySensor. يجب على المحول الخاص بك:
    • قبول LegacySensor في منشئه
    • تنفيذ getTemperature() عن طريق قراءة قيمة الفهرنهايت من المستشعر القديم وتحويلها إلى سيليزيوس باستخدام الصيغة: (fahrenheit - 32) * 5 / 9
  • main.php — قم بتضمين ملف LegacySensorAdapter. ستتلقى مدخلاً واحداً: درجة الحرارة بالفهرنهايت.

    قم بإنشاء LegacySensor باستخدام درجة الحرارة المدخلة، ثم غلفه في LegacySensorAdapter. استدعِ getTemperature() على المحول واطبع النتيجة مقربة إلى منزلتين عشريتين.

    يجب أن يكون تنسيق المخرجات: Temperature: [celsius]C

يوضح هذا التحدي كيف يسمح لك نمط المحول (Adapter Pattern) بدمج الواجهات غير المتوافقة دون تعديل الكود الحالي. يعمل تطبيق الطقس الخاص بك حصرياً مع واجهة TemperatureSensor، وهو غير مدرك تماماً بأن القراءات الفعلية تأتي من مستشعر فهرنهايت قديم.

ورقة مرجعية

يُعد نمط المحول (Adapter Pattern) نمط تصميم هيكلي يسمح للكائنات ذات الواجهات غير المتوافقة بالعمل معاً من خلال العمل كجسر بينها.

البنية الأساسية

قم بتعريف الواجهة التي يتوقعها تطبيقك:

<?php
interface PaymentProcessor {
    public function pay(float $amount): string;
}

فئة (class) من طرف ثالث بواجهة مختلفة:

<?php
class StripeAPI {
    public function createCharge(int $cents, string $currency): string {
        return "Charged {$cents} {$currency} via Stripe";
    }
}

إنشاء المحول

يقوم المحول بتنفيذ الواجهة المتوقعة بينما يستخدم داخلياً فئة الطرف الثالث:

<?php
class StripeAdapter implements PaymentProcessor {
    public function __construct(private StripeAPI $stripe) {}
    
    public function pay(float $amount): string {
        $cents = (int)($amount * 100);
        return $this->stripe->createCharge($cents, 'USD');
    }
}

الاستخدام

$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
// المخرجات: Charged 2999 USD via Stripe

يقوم المحول بالترجمة بين الواجهات، مما يسمح لكود تطبيقك بالبقاء دون تغيير عند دمج مكتبات خارجية أو تبديل التنفيذات.

جرّب بنفسك

<?php
require_once 'LegacySensorAdapter.php';

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

// TODO: إنشاء LegacySensor باستخدام درجة الحرارة المدخلة
// TODO: تغليفه في LegacySensorAdapter
// TODO: استدعاء getTemperature() على المحول (adapter)
// TODO: طباعة النتيجة مقربة إلى منزلتين عشريتين
// تنسيق المخرجات: Temperature: [celsius]C

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

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

جميع دروس Object Oriented Programming