نمط المحول
جزء من قسم 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 المحددة. إذا قمت لاحقاً بالتبديل إلى مزود دفع مختلف، فستقوم ببساطة بإنشاء محول جديد دون تغيير الكود الحالي الخاص بك.
التحدي
سهللنقم ببناء نظام لتحويل درجات الحرارة باستخدام نمط المحول (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
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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)