اینترفیس ها (interface) در PHP

PHP OOP Interfaces

درسنامه درس 9 از سری شی گرایی در PHP
php-oop-interfaces

اینترفیس ها (interfaces) از یک نظر شبیه به کلاس های انتزاعی هستند؛ هر دوی آنها دارای متدهای انتزاعی هستند که که در کلاس های فرزند باید بدنه نویسی شوند. از این نظر می توان گفت، اینترفیس ها به نظم کد کمک بزرگی می کنند چرا که کلاس های فرزند را مجبور به بدنه نویسی برای متدهای خودشان می کنند.

استفاده از اینترفیس ها معمولا در کار های تیمی نمود پیدا می کند؛ زمانی که مدیر پروژه می خواهد مطمئن شود که تک تک برنامه نویسان و توسعه دهندگان، متدهایی که به آن ها واگذار شده را خواهند نوشت.

البته می توان از اینترفیس ها جهت نظم در پروژه های شخصی نیز استفاده کرد. به عنوان مثال شما می توانید با استفاده از اینترفیس ها خود را ملزم به نوشتن متدها کنید.

چطور اینترفیس بسازیم؟

برای ساخت اینترفیس ها از کلمه interface استفاده می کنیم. کلاسی که از یک اینترفیس ارث می برد با کلمه implements مشخص و تعیین می شود. به مثال زیر دقت کنید:

interface interfaceName { 
  // متود های انتزاعی در این قسمت قرار می گیرند
}
 
class Child implements interfaceName {
  // این قسمت متدهای اینترفیس را تعریف می کند و ممکن است کدهای خاص خودش را نیز داشته باشد
}

این مثال قالب کلی برای تعریف یک اینترفیس و فرزندش را به ما نشان می دهد.

حال در مثال زیر می خواهیم برای کلاس هایی که مسئول مدیریت ماشین ها هستند، اینترفیسی تعریف کنیم که تمام کلاس های فرزند خود را ملزوم به استفاده از متدهای ()setMethod و ()getMethod می کند:

interface Car { 
  public function setModel($name);
  
  public function getModel();
}

اینترفیس ها مانند کلاس های انتزاعی شامل متدهای انتزاعی و ثابت ها (constants) هستند اما بر خلاف کلاس های انتزاعی فقط می توانند متدهای public داشته باشند و از طرفی نمی توانند هیچ متغیری داشته باشند.

همانطور که گفتیم کلاس هایی که از اینترفیس ارث بری دارند، موظف هستند تمام متدهایی که در اینترفیس وجود داشته است را کدنویسی کنند، و این مسئله شامل پارامتر ها هم می شود.

اگر نمی دانید پارامتر چیست به قسمت آخر مقاله زیر مراجعه کنید:

به همین دلیل کلاس miniCar در مثال زیر باید تک تک متدها را بدنه نویسی کند:

class miniCar implements Car {
  private $model; 
   
  public function setModel($name)
  { 
    $this -> model = $name; 
  }
  
  public function getModel()
  {
    return $this -> model; 
  }
}

آیا می توان در یک کلاس چندین اینترفیس تعریف کرد؟

بله شما می توانید در یک کلاس بیش از چندین اینترفیس داشته باشید. حتما از خودتان می پرسید چرا به چنین چیزی نیاز داریم؟ اگر یادتان باشد هر کلاس فرزند فقط می توانست از یک کلاس پدر ارث بری داشته باشد. اینترفیس ها این مشکل را دور می زنند. برای نمایش چنین مثالی یک کلاس به نام Vehicle می سازیم.

interface Vehicle {
  public function setHasWheels($bool); 
 
  public function getHasWheels();
}

حالا کلاس فرزند ما در مثال زیر می تواند از هر دو اینترفیس ارث بری داشته باشد:

class miniCar implements Car, Vehicle {
  private $model; 
  private $hasWheels; 
  
  public function setModel($name)
  { 
    $this -> model = $name; 
  }
  
  public function getModel()
  {
    return $this -> model; 
  }
  
  public function setHasWheels($bool)
  { 
    $this -> hasWheels = $bool; 
  }
  
  public function getHasWheels()
  {
  return ($this -> hasWheels)? "has wheels" : "no wheels";
  }
}

تمامی ساختار این کدها، در مثال های دیگر، در جلسات قبل توضیح داده شده اند اما اگر با ساختار خط آخر آشنا نیستید باید بدانید این یک اپراتور ternary است. در مورد این اپراتور بیشتر بخوانید.

سوال بنیادین: چه زمانی از اینترفیس ها استفاده کنیم و چه زمانی از کلاس های انتزاعی؟ اصلا فرقشان چیست؟

پاسخ: بگذارید با یک مثال واقعی به خودمان یادآوری کنیم که اصلا فایده کلاس های انتزاعی چه بود. فرض کنید من یک سیب به شما می دهم:

<?php 

class Fruit {
    private $color;

    public function eat() {
        // جویدن
    }

    public function setColor($c) {
        $this->color = $c;
    }
}

class Apple extends Fruit {
    public function eat() {
        // جویدن تا هسته
    }
}

class Orange extends Fruit {
    public function eat() {
        // پوست کردن
        // جویدن
    }
}

اگر بعد از اینکه یک سیب به شما دادم و شما آن را خوردید از شما بپرسم چه مزه ای داشت، به من چه می گویید؟ قاعدتا شما می گویید که مزه سیب می دهد! حالا به کد زیر نگاه کنید:

<?php 
$apple = new Apple();
$apple->eat();

// حالا به شما یک میوه می دهم
$fruit = new Fruit();
$fruit->eat();

در کد بالا به شما یک میوه (fruit) دادم. چه مزه ای می دهد؟ طبیعتا این سوال جواب منطقی ندارد! چه میوه ای؟ میوه که یک مفهوم است و به خودی خود مزه ندارد!

بنابراین در کدها نباید اجازه داشته باشیم تا از کلاس پدر که fruit است، شیء بسازیم چرا که منطقی نیست. ما در قسمت های قبلی این مثال را با یک فروشگاه زدیم و به شما گفتیم اینجاست که از کلاس های انتزاعی (abstract) استفاده می کنیم:

<?php 
abstract class Fruit {
    private $color;

    abstract public function eat(){}

    public function setColor($c) {
        $this->color = $c;
    }
}
?>

بنابراین در کلاس های انتزاعی می توانید متدها را به صورت انتزاعی تعریف کنید اما در اینترفیس ها متدها همگی انتزاعی هستند.

به علاوه، در کلاس های انتزاعی می توان متدهای کاربردی (functional) داشت اما در اینترفیس ها متدها نمی توانند بدنه داشته باشند. در واقع بدنه شان خالی است و کاری انجام نمی دهند.

منظور از متد کاربردی متدی است که واقعا کاری انجام می دهد و در قسمت بدنه کدهایی دارد. به همین ترتیب یک متغیر کاربردی یعنی یک متغیر واقعی که به طور مثال در خودش مقداری ذخیره می کند. در مثال های بالا و اینترفیس هایی که تعریف کرده ایم مشاهده می کنید که متدها در اینترفیس ها بدنه ندارند و فقط تعریف شده اند. مثال:

interface Vehicle {
  public function setHasWheels($bool); 
 
  public function getHasWheels();
}

در مثال بالا، متدهای getHasWheels و setHasWheels خالی هستند و هیچ کاری نمی کنند تا زمانی که در کلاس فرزند تعریف شوند.

خلاصه تفاوت ها بین اینترفیس و کلاس انتزاعی

تفاوت بین interface ها و کلاس های abstract برای شما در جدول زیر به طور خلاصه تهیه شده است:

نقطه نظر / دسته بندی اینترفیس ها کلاس های انتزاعی
از نظر کد * متدهای انتزاعی

* ثابت ها (constants)

* متدهای انتزاعی

* ثابت ها (constants)

* متدهای کاربردی (functional)

* متغیر های کاربردی

از نظر access modifier public public

protected

private

و ...

تعداد کلاس های پدر یک کلاس می تواند بیش از یک اینترفیس داشته باشد کلاس فرزند فقط از یک پدر ارث بری دارد

بنابراین در دنیای واقعی و هنگام انجام پروژه های واقعی:

  • زمانی که می خواهید توسعه دهندگان تیم شما (و خودتان) حتما در کلاس هایی که می سازند از تعداد خاصی متد استفاده کنند، از اینترفیس ها استفاده کنید.
  • زمانی که می خواهید توسعه دهندگان تیم شما (و خودتان) حتما در کلاس هایی که می سازند از تعداد خاصی متد استفاده کنند و می خواهید خودتان چند متد پایه ای را نیز تعریف کنید تا در صورت تمایل از آن ها استفاده کنند، از کلاس های انتزاعی استفاده کنید.

در ضمن اگر از جلسات قبل به خاطر داشته باشید، اگر ما با شرایط خاص (مانند استفاده از access modifier ها یا انتزاعی کردن متدها) جلوی کلاس های فرزند را نمی گرفتیم، آن ها می توانستند کدهای کلاس پدر را override کنند اما از آن جایی که اینترفیس ها نمی توانند کدهای کاربردی و فعال داشته باشند، چیزی برای override کردن وجود ندارد و هر فرزند خودش (مانند متدهای انتزاعی) بدنه کدهای خود را بنویسد.

امیدوارم از این قسمت استفاده مفید را برده باشید. در قسمت بعد به سراغ مبحث Polymorphism می رویم.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری شی گرایی در PHP توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (4 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد
11 تیر 1401
کاملا غیر حرفه ای توضیح داده شده.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
اگر ایرادات مقاله رو بفرمایید ممنون میشیم.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

سمیرا
09 شهریور 1399
سلام خیلی وقته میخوام شی گرایی یاد بگیرم ولی همه مطالب پراکنده و ناقص بود شما خیلی ساده و روان با تمام نکات توضیح دادید ممنونم بابت آموزش خوبتون خیلی استفاده کردم

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
10 شهریور 1399
از همراهی شما سپاسگزاریم. خوشحالیم که آموزش برای شما مفید بود.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

منصور
09 مهر 1398
با سلام سوالی که همیشه ذهنم رو مشغول کرده پیداده سازی شی گرایی در دیتابیس mysql است مثلا ارث بری رو چطور پیاده سازی کنیم و یا برعکس در دیتابیس اشیاء مختلف داریم که در جدول مشترک دیگری با هم ارتباط برقرار می کنند مثلا کتاب داریم و ناشر؛ که ناشر یک شی مستقل است اما با کتاب در ارتباط است و یک خصوصیت از آن می باشد اگر در این زمینه راهنمایی نمایید و یا مقاله و مطلبی است معرفی کنید با تشکر

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
10 مهر 1398
سلام دوست عزیز، مبحث OOP (برنامه نویسی شیء گرا) مخصوص زبان برنامه نویسی هست نه پایگاه داده. در واقع دستور العمل خاصی برای شیء گرا کردن پایگاه داده وجود نداره چون MySQL همچین قابلیتی رو نداره و نیازی هم نیست به چنین قابلیتی! مثلا به قول شما کتاب داریم و ناشر؛ بهترین راه اینه که با استفاده از Foreign Key این دو تا ستون رو به هم متصل کنیم. زمانی که داریم در سمت PHP برنامه رو مینویسیم واقعا نیازی به چیز دیگه ای نداریم، این بیشتر مربوط به کدنویسی PHP شما میشه تا جدول های MySQL البته اگر منظورتون OODB هست، به مقاله ی زیر مراجعه کنید: https://study.com/academy/lesson/what-is-an-object-oriented-database.html

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

منصور
11 مهر 1398
ممنون از پاسختون مشکل من اینجاست که وقتی می خواهیم داده های یک کتاب را از دیتابیس بخوانیم ابتدا باید کتاب را بخوانیم و مجددا با استفاده از id ناشر اطلاعات ناشر را برای نمایش صدا بزنیم (PDO رو هنوز مطالعه نکردم) آیا روش خاصی است که بتوانیم به یکباره همه آنها دریافت کنیم بدون اینکه نیاز باشه در دیتابیس view ایجاد کنیم؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
11 مهر 1398
متوجه مشکل شما شدم. من فرض میکنم که با زبان SQL آشنایی کامل دارید. برای حل مشکل شما چند راه حل وجود داره: 1- اگر طراحی جدول ها خوب باشه تا حد زیادی از بروز چنین موقعیت هایی جلوگیری میشه بنابراین باید به طراحی پایگاه داده اهمیت زیادی داد. مثلا میتونیم جدولی برای کتاب داشته باشیم که توی اون هم «عنوان کتاب»، هم «ناشر کتاب» (مثلا به عنوان Foreign Key)، هم «متن کتاب» و ... وجود داشته باشه. در عین حال ناشر کتاب میتونه یه جدول جداگانه هم داشته باشه که اطلاعات بیشتر رو درون اون قرار میدیم (سال تاسیس انتشارات، مدیریت، تلفن تماس و ...) ولی به هر حال نام انتشارات رو به عنوان ستون foreign key در جدول کتاب ها داریم. 2- اما به هر حال این حالت ها پیش میان و این موقعیت رو میتونید با استفاده از دستورات UNION و JOIN در هنگام کوئری زدن به پایگاه داده حل کنین. این دو دستور هر دو مربوط به SQL هستن و میتونین توی لینک های زیر باهاشون بیشتر آشنا بشین: https://www.roxo.ir/all-sql-join-commands/ https://www.roxo.ir/union-and-group-by-sql-commands/ کار هر دو دستور ادغام کردن دو کوئری (یک کوئری برای کتاب و یک کوئری برای ناشر) در یک کوئری هست و یا ادغام کردن نتیجه ها در یک نتیجه است. 3- سیستم های مدیریت پایگاه داده ی رابطه ای (relational database management systems) مثل MySQL رو کلا کنار بزارید و وارد سیستم های مدیریت پایگاه داده ی شیء گرا (object-oriented database management system) بشین. بنده آنچنان آشنایی با این نوع سیستم ها ندارم ولی به احتمال زیاد میتونن مشکل شما رو حل کنن.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

Ahmad Tavakoli
22 دی 1397
سلام آقای زوارمی میشه توضیح بدین access modifier چی هست؟ من یادم نمیاد توضیح داده باشین...

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
23 دی 1397
سلام به شما، access modifier ها همون کلید واژه های public و private و protected و ... بودند. توی همین جدولی که داخل مقاله هست اگه دقت کنید میشه فهمید. روبروی access modifier ها حالت های مختلفش رو گذاشتم. البته قبلا هم به صورت خلاصه در مقاله ی زیر بهش اشاره کرده بودم: https://www.roxo.ir/inheritance-in-php-object-oriented-programming/ وارد مقاله که شدید با کلید های Ctrl + F عبارت "در حال کار با مبحث دسترسی (access) هستیم که به نوبه ی خود ما را به سمت access modifier ها هدایت می کند" را سرچ کنید تا قسمت مورد نظر رو براتون پیدا کنه.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.