php 101 التجريد (Abstraction) والواجهات (Interfaces) والسمات (Traits) في PHP


عبد اللطيف ايمش

abstraction-interfaces-traits-in-php.png

التجريد – Abstraction

نعلم أنَّ الصنف المُشتَق يأخذ خاصياته من الصنف الأب لكن الصنف المُشتَق مستقل تمامًا عن الصنف الأب؛ وقد يكون في بعض الأحيان من الجيد أن نرسم خطوطًا عريضة لآلية سلوك الصنف الابن، وهذه هي مهمة الأصناف والدوال المجردة. إذ أنَّ الصنف المجرد يحتوي على دوال غير مكتملة (أي مجردة) التي يجب أن يملأها الابن لكي يكون صنفًا وعدا ذلك سيكون صنفًا مجردًا أيضًا. 

بكلامٍ آخر، الصنف المُجرَّد (Abstract Class) هو صنف يحتوي على أسماء دوال دون كتابة الشيفرات المسؤولة عن عملها وتسمى تلك الدوال بالدوال المجردة، وقد يحتوي أيضًا على دوال كاملة اعتيادية تؤدي وظيفتها تمامًا. انظر إلى المثال الآتي لمزيدٍ من الإيضاح:

<?php
// تعريف صنف مجرد
abstract class AbsClass
{
  function __construct()
  {
    echo "this is an abstract class <br>";
  }
  // دالة مجردة
  abstract public function abs_function();
  function full_function()
  {
    echo "this is not an abstract function <br>";
  }
}
class SubClass extends AbsClass
{
  function __construct()
  {
    echo "this is child class <br>";
    parent::full_function();
  }
  // تعريف الدالة المجردة
  public function abs_function()
  {
    echo "this is completed abstract function <br>";
  }
}
$obj = new SubClass();
$obj->abs_function();
?>

نستعمل الأصناف المجردة عندما يلزمنا إنشاء طريقة محددة للتعامل مع عدِّة أصناف مُشتقَّة، التي قد تتشارك ببعض الوظائف. 

ملاحظة: لا يمكن إنشاء كائن من صنف مجرد، حيث لا يمكن إلا اشتقاق تلك الأصناف. يستعمل الصنف المجرد لتقييد عمل الصنف الابن.

الواجهات interfaces

من بين الحالات التي نستعمل فيها الواجهات (interfaces) هي عندما نريد تطبيق التعددية الشكلية أي أن تكون طريقة تعاملنا متشابهة مع عدِّة أصناف. 

الواجهة هي مجموعة من الدوال المجردة أي أنك تعرف اسم الدالة مع المعاملات التي تقبلها لكن دون تحديد طريقة عمل الدالة، ويمكن للصنف أن يستعمل أكثر من واجهة، لكن يجب أن يعيد تعريف كل الدوال الموجودة في تلك الواجهة، انظر إلى المثال الآتي لأخذ فكرة عن الواجهات:

<?php
// تعريف واجهة
interface MyInterface
{
  // abstract functions 
  // all must be public
  public function display();
  public function another($argument);
}
// واجهة أخرى
interface AnotherInterface
{
  public function complete_it();
  public function one_more();
}
class Parent
{
  function parent_fun()
  {
    echo "parent function";
  }
}
// صنف يشتق صنفًا آخر ويستعمل واجهة
class Demo extends Parent implements MyInterface, AnotherInterface
{
  public function display()
  {
    echo "display complete";
  }
  public function another($argument)
  {
    #code
  }
  public function complete_it()
  {
    #code
  }
  public function one_more()
  {
    #code
  }
}
?>

نستعمل الواجهات عندما نريد إنشاء طريقة موحدة للتعامل مع عدِّة أصناف، فمثلًا، نُنشِئ واجهة اسمها Database فيها دوال مجردة مثل select و insert وغيرها، ثم نستعمل تلك الواجهة في صنف MySQL وفي صنف SQLite، ونعيد تعريف الدوال الموجودة في الواجهة بما يلائم طريقة عمل كل نوع من أنواع قواعد البيانات. وبهذه الطريقة نستطيع أن نستعمل قواعد بيانات MySQL أو SQLite بنفس الآلية تمامًا. 

ملاحظة: يجب أن تكون جميع الدوال داخل الواجهة عامةً، يمكن أن يرث صنفٌ ما صنفًا آخر ويستعمل واجهة بنفس الوقت، لكن يجب أن يكون تعريف الاشتقاق قبل الواجهات.

السمات Traits

قدم الإصدار 5.4.0 من PHP ميزة السّمات Traits، وهي طريقة تسمح بإعادة استعمال الشيفرات في اللغات التي لا تسمح بالوراثة المتعددة، وهي تقلل من المحدوديات الناتجة عن عدم السماح بالوراثة المتعددة عن طريق إتاحة استعمال مجموعة من الدوال في عدة أصناف. 

أي لو كانت عندك مجموعة من الدوال العامة، وترغب في مشاركتها بين أكثر من صنف، فضعها في Trait ثم استعملها (use) في تلك الأصناف.

يُعرف Trait عبر ذكر الكلمة المحجوزة trait متبوعةً باسمه، ثم تُعرَّف الدوال داخله. وتُستعمل الكلمة use عند تعريف صنف يستعمل Trait معين كما في المثال الآتي:

<?php
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}
class World {
    use HelloWorld;
}
$obj = new World();
$obj->sayHello(); // ستُطبع عبارة Hello World!
?>

يمكن إعادة تعريف الدوال داخل الأصناف التي تستعمل Trait معيّن، كما في المثال الآتي:

<?php
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}
class World {
    use HelloWorld;
}

class NewWorld {
    use HelloWorld;
    public function sayHello() {
        echo 'Hello Universe!';
    }
}
$obj1 = new World();
$obj1->sayHello(); // ستُطبع عبارة Hello World!

$obj2 = new NewWorld();
$obj2->sayHello(); // ستُطبع عبارة Hello Universe!
?>

يمكن استعمال أكثر من Trait في نفس الصنف عبر ذكر أسمائهم في عبارة use مفصولًا بينهم بفاصلة، لاحظ أنه إذا عُرِّفَت دالتين بنفس الاسم في أكثر من Trait، ثم استعملناها في صنف، فسيحدث تضارب وتظهر رسالة خطأ fetal error، ويمكنك حل مشكلة التضارب في الأسماء عبر استعمال المعامل insteadof أو عبر as كما في المثال الآتي:

<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    // لدينا في A و B دالتين اسمهما bigTalk و smallTalk
    // ما يلي سيجعل الصنف يستعمل الدالة smallTalk من B عوضًا عن مثيلتها في A
    // و bigTalk من A عوضًا عن B
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        // لاحظ كيف استعملنا as لتغير اسم الدالة في الصنف
        B::bigTalk as talk;
    }
}
?>

مصادر





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن