در برنامه نویسی تیمی گاهی نیاز میشود هر عضو گروه، یک کلاس از نوعی خاص را پیادهسازی کند که معمولا هر برنامه نویس استاندارد خاص خود را برای نامگذاری توابع و متغیرهای کلاس دارد. این روند باعث ایجاد بههمریختگی و ناخوانایی در کدها میشود. برای جلوگیری از ایجاد این مشکل در برنامه نویسی شی گرا (Object-Oriented) مفهومی به نام Interface به وجود آمده است که در ادامه قصد توضیح آن را داریم.
interface چیست
اینترفیس (Interface) مفهومی مشابه کلاس است با این تفاوت که نمیتوان در آن متدی را پیادهسازی کرد. در واقع اینترفیس فقط مجموعهای از قراردادها (contract) است که متدهای مختلف بدون تعریف بدنه (Body) و عملکرد در آن تعریف شده اند و کلاسی که این اینترفیس را پیادهسازی میکند، باید درون خود از آن تابع استفاده کرده و برای آن عملکرد تعریف کند. فراموش نکنید که هر کلاس میتواند بیشتر از یک اینترفیس را پیادهسازی کند. به عنوان مثال در کد PHP زیر یک اینترفیس به نام شکل یا Shape را پیاده سازی کرده ایم:
interface Shape {
public function getArea();
}
این به آن معنی است که از این پس هر کلاسی که اینترفیس Shape را پیادهسازی کند ملزم به پیادهسازی متد getArea() در خود است. در کد زیر دو کلاس Square یا مربع و Rectangle یا مستطیل اینترفیس Shape را پیاده کردهاند:
class Square implements Shape {
private $width;
public function __construct($width)
{
$this->width = $width;
}
public function getArea()
{
return $this->width * $this->width;
}
}class Rectangle implements Shape {
private $width;
private $height;
public function __construct($width , $height)
{
$this->width = $width;
$this->height = $height;
}
public function getArea()
{
return $this->width * $this->height;
}
}
ممکن است این سوال برایتان پیش بیاید که چرا در این مثال از Interface استفاده کردیم در حالی که میتوانستیم به راحتی کلاسی به نام Shape داشته باشیم که طول و عرض هر شکل را در سازنده یا Constructor خود دریافت کرده و سپس در متد getArea() پیادهسازی کند. در این حالت کافی بود بقیه کلاسها از آن به ارث ببرند و بر اساس قانون وراثت صاحب تمام متدها و ویژگیهای Shape شوند. در آن صورت کلاس Shape و کلاسهای فرزند آن به این شکل نوشته میشد:
class Shape {
private $width;
private $height;
public function __construct($width , $height)
{
$this->width = $width;
$this->height = $height;
}
public function getArea() {
return $this->width * $this->height;
}
}
فرض کنید میخواهیم کلاسی به نام Circle یا دایره را در مجموعه کلاسهای اشکال خود داشته باشیم. همانطور که میدانید مساحت دایره برخلاف مستطیل و مربع بر اساس طول و عرض نیست بلکه بر اساس شعاع آن است. درحالیکه متد getArea() در کلاس والد آن یعنی Shape بر اساس طول و عرض پیادهسازی شده است! بنابراین میبینید که بهترین گزینه برای این مثال استفاده از اینترفیس بوده است. با استفاده از اینترفیس کلاس Circle به راحتی به صورت زیر پیاده میشود:
class Circle implements Shape {
private $radius;
public function __construct($radius)
{
$this->radius = $radius;
}
public function getArea()
{
return $this->radius * (3.14 ** 2);
}
}
$circle = new Circle(2);
echo $circle->getArea();
پس از پیادهسازی اینترفیس Shape در صورتی که تمام متدهای آن را در کلاسی که از این اینترفیس استفاده کرده، پیادهسازی نکنیم، یک خطای مهلک (Fatal Error) دریافت میکنیم و برنامه متوقف میشود.
یک مثال کاربردی از Interface
بهترین روش پیادهسازی اتصال به پایگاه داده در هر برنامه روشی است که وابسته به نوع پایگاه داده و نوع پیادهسازی اتصال به آن نباشد. با این جملات احتمالا به راحتی متوجه به اهمیت و کاربرد اینترفیس در این مثال شدهاید. در این مثال با استفاده از اینترفیس تمام پیادهکنندهها یا درایورهای پایگاه داده مثل mySQL و SQLServer را ملزم به داشتن متدهای connect و query میکنیم. در واقع با استفاده از اینترفیس در این نمونه دیگر فرقی نمیکند که بعدها از چه روشی برای اتصال به پایگاهداده استفاده میشود. روش دسترسی به دادهها همیشه یکی است!
interface SQLDriver {
public function connect();
public function query($query);
}
class MySQL implements SQLDriver {
public function connect()
{
// connecting to a mysql database
}
public function query($query)
{
echo $query;
// query to a mysql database
}
}
class SQLServer implements SQLDriver {
public function connect()
{
// connecting to a sqlServer database
}
public function query($query)
{
// query to a sqlServer database
}
}
حال برای استفاده از پایگاه داده کافی است یک کلاس واسط مثلDatabaseConsumer داشته باشیم و شی درایور پایگاه داده مورد نظر خود را به آن پاس دهیم تا همه چیز به صورت اتوماتیک انجام شود:
class DatabaseConsumer {
private $provider;
public function __construct(SQLDriver $sqlDriver)
{
$this->provider = $sqlDriver;
$this->provider->connect();
}
public function query($query) {
$this->provider->query($query);
}
}
$dc = new DatabaseConsumer(new MySQL());
$dc->query("SELECT * FROM `posts`");
در مثال بالا در متد سازنده یا __construct() شی SQLDriver که در واقع همان شی درایور پاس داده شده به DatabaseConsumer است، در اتریبیوت provider این کلاس ذخیره میشود. با این کار از این پس به تمام متدهای public درایور مورد نظر دسترسی داریم.
حال کافی است برای راهاندازی پایگاه داده در متد سازنده با استفاده از متد connect درایور مورد نظر، به آن متصل شویم. سایر متدهای کلاس واسط نیز با اتصال به متدهای متناسب در کلاس درایور داده شده، عمل درخواست شده را انجام میدهند. حال در خارج از کلاس، برای استفاده از پایگاه داده کافی است یک شی از کلاس درایور مورد نظر را ایجاد کرده و به سازنده کلاس DatabaseConsumer پاس دهیم.
جمعبندی
همچون وراثت، مفهوم Interface نیز یکی از مباحث ابتدایی و مهم در یادگیری برنامه نویسی شی گرا است که امیدواریم به شما در فهم آن کمک کرده باشیم. افراد زیادی به دلایل مختلفی مانند علاقمندی یا کسب درآمد از طریق برنامه نویسی به دنبال یادگیری زبانهای مختلف هستند. فراموش نکنید سون لرن در مسیر یادگیری برنامه نویسی همواره در کنار شما است.
۱۸ دیدگاه
۲۹ بهمن ۱۴۰۱، ۱۰:۴۳
سلام خیلی مفید بود
۱۷ تیر ۱۴۰۱، ۰۷:۱۰
عالی
علی۲۷ مهر ۱۴۰۰، ۱۹:۵۴
با این حساب یعنی اینترفیس هیچ کاربردی نداره.
چون وقتی دوباره باید اون تابعها را در کلاس مربوطه ایجاد کنیم پس چرا باید از یک اینترفیس استفاده کنیم.
نازنین کریمی مقدم۲۸ مهر ۱۴۰۰، ۰۲:۰۲
درود
اینترفیس بیشتر در مواقعی قدرتمند هست که برنامه بسیار بزرگ باشه و بخوایم از تغییر بیمورد کدهای قبلی خودداری کنیم.
جز اون باهاتون موافقم و اگر علاقه داشتید میتونید چندتا کامنت پایینتر رو هم بخونید.
پوریا۰۸ تیر ۱۴۰۰، ۱۰:۲۱
عالی بود . تشکر
مهدی۲۰ شهریور ۱۳۹۹، ۱۵:۳۳
مثال پایگاه داده خیلی خوب بود و به درک بهتر مسئله خیلی کمک میکنه ممنونم ازتون
نازنین کریمی مقدم۲۲ شهریور ۱۳۹۹، ۰۹:۰۳
سلام. ممنون که با ما همراه هستید.
امیرحسین گروسی۳۱ تیر ۱۳۹۹، ۱۲:۱۱
خب به راحتی میشه در کلاسی که shape نامگذاری شده یک مقدار ورودی دیگر علاوه بر width و height ایجاد کرد به نام shapeName و برای هر شکل عملیات مخصوصشو انجام بدیم ... برخلاف interface استفاده class به نوشتن کد تمیز (clean code) کمک میکنه
نازنین کریمی مقدم۳۰ شهریور ۱۳۹۹، ۱۹:۳۱
درود خداوند بر شما. به شدت با حرفتون موافقم!
خود منم خیلی خیلی کم از اینترفیس استفاده میکنم و اینکه کد تمیز میمونه بهم حس خوبی میده. اما باید توجه کنیم که کاربرد اینترفیس بیشتر برای کم کردن دسترسیها و تست راحتتر هست و ما که از کلاس استفاده میکنیم، این دو نکته مهم رو نادیده میگیریم.
اگر جایی به مشکل خوردم که نیاز به اینترفیس بود و از نوشتن کلاسم پشیمون بودم، حتما برمیگردم و اینجا توضیحشو مینویسم.
محمد رحمتی۱۵ اسفند ۱۳۹۸، ۱۸:۲۵
بسیا عالی ممنون از زحمت شما. سپاس
فرانک خوابستان۲۲ آبان ۱۳۹۸، ۰۸:۵۹
خیلی مفید بود ممنون از شما
ریحانه یزدانی۲۲ آبان ۱۳۹۸، ۱۱:۵۰
سلام
ممنون از شما برای وقتی که گذاشتین :)
ایلیا۲۶ فروردین ۱۳۹۸، ۰۵:۵۴
پیاده سازی پایگاه داده اخرش چه جالب بود
نمیشه کاری کرد که کلاس DatabaseConsumer خودش شی از کلاس درایور بسازه و کارا اتوماتیک شه؟
زهرا فرحمند۲۸ فروردین ۱۳۹۸، ۰۷:۵۹
سلام ایلیای عزیز.
کلاس DatabaseConsumer رو برای رسیدن به اصل OpenClosed در سالید نوشتیم پس دستکاری کلاس برای تغییر درایور از دیدگاه سالید غیر قانونیه. پس به هر حال درایور باید از بیرون کلاس بهش پاس داده بشه. اگر با اصول SOLID آشنا نیستید میتونید مقالات بعدی در این مورد رو مطالعه کنید. ممنونم از توجهتون :)
امیرحسین ضیایی۲۴ فروردین ۱۳۹۸، ۰۴:۲۹
مرسی از مقالتون
زهرا فرحمند۲۵ فروردین ۱۳۹۸، ۰۵:۰۳
مرسی از همراهی شما آقای ضیایی عزیز :)
امیر حسین عزیزیان۲۲ فروردین ۱۳۹۸، ۱۲:۰۶
خیلی مهمه که درباره یه موضوع ، مفهوم ، مسئله یا هر چیزی که اسمشو میذاریدتر و تمیز توضیح داده بشه . ب نظرم این مطلب این ویژگی رو داشت