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

درسنامه درس 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 توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما (3 دیدگاه)

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

سمیرا
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 ها هدایت می کند" را سرچ کنید تا قسمت مورد نظر رو براتون پیدا کنه.

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