پیاده سازی چند ریختی با استفاده از interface


لغت interface  یکی از پیچیده ترین و در عین حال بهترین ابزار جهت برنامه نویسی شی گراست. این کلمه نماینگر یکی از چهار رکن اصلی این نوع برنامه نویسی یعنی حالت چند ریختی است (Polymorphism).

منظور از Interface  یا protocol پایبندی به یک قرار داد از طرف کلاس است . یعنی کلاس تعهد می دهد که همواره از method  و property  های interface پیروی کند .syntax ایجاد interface  در زبانهای برنامه نویسی متفاوت است . مثلا در php  از interface  و یا در swift  از کلمه protocol استفاده می شود. پس مراحل را قدم به قدم و با زبان php  به پیش می رویم .

فرض کنید قصد داریم یک سیستم لاگر بنویسیم. خب تصمیم گیری می شود که این سیستم تمامی اطلاعات را داخل فایل قرار دهد ، پس :

 class LogToFile{

public function execute($message){

       echo &quotSave to &quot . $message;

  }

}

کلاسی ایجاد کردیم با یک متد که وظیفه اش ذخیره سازی است ( برای کد کمتر این متد فقط یک پیغام چاپ می کند ).

حالا فرض می کنیم یک کلاس ایجاد کردیم و می خواهیم هر وقت شی از این کلاس ساخته شد پیغام لاگ ذخیره شود . پس :

class SMS {

protected $logger;

public function __construct(LogToFile $logger){

       $this->logger=$logger;

   }

  public function sendSMS(){

         echo “sending sms”;

   }

}

خوب همانطور که ملاحظه می کنید هر بار که یک شی جدید از این کلاس ایجاد شود به صورت اتوماتیک یک لاگ جدید بر روی فایل ایجاد می شود

فرض کنیم که مدتی گذشت و مدیر سیستم خواست برای گرفتن گزارشات بهتر به جای ذخیره سازی لاگ ها بر روی فایل ، اطلاعات بر روی دیتا بیس ذخیره گردد.

در اینجاست که مشکلات پیدا می شود . باید یک کلاس جدید ایجاد کنیم و متد execute را ایجاد کنیم و به جای کلاس LogToFile از کلاس جدیدی استفاده کنیم . این یعنی کلی جستجو و کلی عذاب  و پیدا کردن تمام کلاس هایی که LogToFile  رو در constructor  استفاده کردند !!پس باید چیکار کرد؟؟

قانون طلایی می گوید :

بر روی interface  برنامه نویسی کنید نه بر روی Implementation

خب بگذارید این قانون طلایی را بر اساس کد باز کنیم:

ابتدا یک interface ایجاد می کنیم:

interface Logger{

public function execute($message);

}

همانطور که می بینید یک اینترفیس ایجاد کردیم که از لحاظ syntax  مثل کلاس است. تنها با دو تفاوت :

  • به جای واژه کلاس از interface استفاده کردیم.
  • متد را تعریف کردیم و بدنه داخلی آنرا و اینکه چه کار را دقیقا باید انجام بدهد خالی گذاشتیم.

حالا دو کلاس ایجاد  می کنیم یکی برای ذخیره بر روی فایل و دیگری ذخیره بر روی دیتابیس با این تفاوت که در اینجا می گوییم که این کلاس حتما باید به interface ما یعنی logger متعهد باشد

class LogToFile implements Logger {

public function execute($message){

      echo &quotSave to &quot . $message;

    }

}

class LogToDatabase implements Logger {

      public function execute($message){

          echo &quotSave to &quot . $message;

       }

}

خوب پس کلاس های ما متعهد شدند که متد execute را دارا باشند ولی اینکه این متد چه کاری را انجام دهد بسته به خود کلاس است.

حالا در کلاس ها در قسمت construct به جای ساختن شی از کلاس LogToFile از interface که ساخته ایم استفاده می کنیم و به constructor می گوییم از هر کلاسی که به تعهد های این interface  پایبند بود استفاده کن .

class SMS{

protected $logger;

public function __construct(Logger $logger){

      $this->logger=$logger;

}

public function send SMS(){

      echo “sending sms”;

}

}

با این کار به کلاس این توانایی را میدهید که در جای مورد نیاز از LogToFile استفاده کنه:

$sms=new SMS(new LogToFile);

و در جای دیگری از LogToDatabase   :

$sms=new SMS(new LogToDatabase);

پس یادمان باشد در جایی که امکان دارد تغییر و تحولاتی در کلاس ایجاد شود ( مثل لاگ یا خواندن از دیتابیس های مختلف و یا ...) از یک interface استفاده کنیم تا در آینده به مشکلات بر نخوریم .

سعی کردم که با استفاده از یک مثال درک بهتری در رابطه با interface  برای شما داشته باشم ولی زمان و مکان استفاده از آن در جاهای مختلف نیاز به تجربه زیاد دارد.