معرفی Interface ها و Abstract کلاس ها

Alternate Text

در حوزه ی برنامه نویسی شی گرا (OOP)، interface ها و abstract کلاس ها ستون های اساسی هستند که توسعه دهندگان را قادر می سازند تا سیستم های نرم افزاری قوی، مقیاس پذیر و قابل نگه داری طراحی کنند. در حالی که هر دو موجودیت هدف مشترک تعریف قابلیت ها برای کلاس ها را دارند، آن ها این کار را به روش متفاوتی انجام می دهند. هدف این آموزش تشریح دقیق این ساختارها، روشن کردن ویژگی ها،کاربردها و انتخاب استراتژیک آن ها برای سناریو های مختلف برنامه نویسی است. در برنامه نویسی تیمی گاهی نیاز می‌شود هر عضو گروه، یک کلاس از نوعی خاص را پیاده‌سازی کند که معمولا هر برنامه نویس استاندارد خاص خود را برای نام‌گذاری توابع و متغیر‌های کلاس دارد. این روند باعث ایجاد به‌هم‌ریختگی و ناخوانایی در کدها می‌شود. برای جلوگیری از ایجاد این مشکل در برنامه نویسی شی گرا (Object-Oriented) مفهومی به نام Interface به وجود آمده است که در ادامه قصد توضیح آن را داریم.

Interface چیست‌؟

یک interface یا واسط، یک مفهومی است که به تعریف متدها بدون پیاده سازی آنها می‌پردازد، بنابراین می‌توان گفت که از یک جهت بسیار شبیه به کلاس های انتزاعی یا Abstract است.

کلاس‌های مختلف می‌توانند از این interface ها ارث بری کنند و به پیاده سازی منطق آن بپردازند.

یک interface می‌تواند شامل تعریف متد ها (Methods)، خصیصه ها (Properties) و رخداد ها (Events) باشد، اما نمی‌تواند دارای فیلد (اعضای خصوصی) باشد.

استفاده از interface ها کار پیاده سازی منطق در برنامه را آسان‌تر کرده و خوانایی کد‌ها را افزایش می‌دهد.

پیاده سازی منطق در interface ها یکی از اصول طراحی SOLID نیز است، همچین در برنامه نویسی شی گرا نیز به شدت مورد استفاده قرار می‌گیرد.

Interface در سی شارپ

یکی از دلایل اصلی استفاده از interface در زبان #C این است که زبان سی شارپ برخلاف زبان C++‎ امکان ارث‌‌ بری چندگانه (Multiple Inheritance) ندارد و تنها از 1 کلاس می‌تواند ارث بری کند.

پیاده سازی واسط (interface) در زبان #C به وسیله‌ی سینتکس رزرو شده‌ی “interface” انجام می‌پذیرد. به طور قراردادی، معمولا در زبان سی شارپ، نام interface ها را با I (آی بزرگ) شروع می‌کنند.

به عنوان مثال فرض کنید می‌خواهیم یک interface برای حیوانات ایجاد کنیم، کد ایجاد این واسط (interface) به صورت زیر خواهد بود:


interface IAnimal
{
  void animalSound(); // interface method (does not have a body)
}

در اینجا ما اینترفیسی با نام IAnimal ایجاد کردیم که در آن یک متد با نام animalSound وجود دارد که مقدار برگشتی آن از نوع void (یعنی متد چیزی Return نمی کند) است.

همانطور که مشاهده می‌کنید، هیچ پیاده سازی برای این متد انجام نشده است زیرا اینترفیس animal یک تعریف انتزاعی است. اکنون کلاس‌های دیگر می‌توانند از این اینترفیس ارث بری کرده و به پیاده سازی متدهای آن بپردازند. به کد زیر توجه کنید:


class Cat : IAnimal
{
  public void animalSound()
  {
	// The body of animalSound() is provided here
	Console.WriteLine("Miow Miow !!");
  }
}
class Dog : IAnimal
		
{
   public void animalSound()
   {
	 // The body of animalSound() is provided here
	 Console.WriteLine("Whoof Whoof !!");
   }
}

در کد بالا دو کلاس Cat و Dog واسط IAnimal را پیاده سازی شده است، که هرکدام از این کلاس‌ها، با توجه به منطق مشخص خودشان، عملیات پیاده سازی را انجام داده‌اند. به عنوان مثال کلاس Cat صدای حیوان را به صورت Miow پیاده سازی کرده است و کلاس Dog به صورت Whoof.

اکنون اگر متد animalSound را در شی‌های ساخته شده از کلاس Cat و Dog صدا بزنیم با نتایج مختلفی روبرو می‌شویم:


internal class Program
{
  static void Main(string[] args)
  {
	Cat persianCat = new Cat();
	Dog shiba = new Dog();
	persianCat.animalSound();
	shiba.animalSound();
  }
}

خروجی این کد به صورت زیر است:

Interfaseimage

باید به نکته توجه کنید که به علت اینکه کلاس‌های Cat و Dog از اینترفیس IAnimal ارث بری کرده‌اند، می‌توان مستقیما به جای ذکر کردن نام کلاس، از نام اینترفیس نیز استفاده کرد:


IAnimal persianCat = new Cat();
IAnimal shiba = new Dog();

نکاتی پیرامون interface در #C

◇ در اینترفیس‌ها تنها توابع تعریف می‌شوند و هیچ پیاده سازی (Implementation) صورت نمی‌گیرد.

◇ به صورت قراردادی نام‌های اینترفیس‌ها با I (آی بزرگ) شروع می‌شود.

◇ همانند کلاس‌های انتزاعی یا Abstract، امکان ساخت یک شی از نوع interface وجود ندارد.

◇ زمانی که یک کلاس، یک interface را پیاده سازی می‌کند، باید تمامی متد‌های تعریف شده درون آن interface را پیاده سازی کند.

◇ تمامی تعاریفی که درون اینترفیس‌ها صورت می‌گیرد به صورت پیشفرض Abstract و Public هستند و به طور کلی یک interface نمی‌تواند شامل اعضای Private باشد.

◇ اینترفیس‌ها نمی‌توانند دارای سازنده یا Constructor باشند.

◇ یک interface می‌تواند از یک یا چند interface دیگر ارث بری (Inheritance) کند.

◇ یک کلاس هیچ محدودیتی در تعداد اینترفیس‌هایی که پیاده سازی می‌کند، ندارد.

جمع بندی

اینترفیس (Interface) یکی از بخش های مهم در برنامه نویسی شی گرا است و بسیاری از اصول برنامه نویسی مانند طراحی SOLID به نوشتن برنامه‌های اینترفیس محور تاکید کرده‌اند، در این مقاله به طور کامل به بحث در مورد اینترفیس‌ها پرداختیم و با نحوه ی پیاده سازی آن آشنا شدیم، همچنین نکاتی که هنگام استفاده از اینترفیس باید به خاطر داشته باشید نیز گردآوری شده‌اند.

معرفی کلاس‌ها و متدهای abstract

هنگامیکه یک برنامه را می‌نویسیم و کلاس‌ها و اعضای آن را مشخص می‌کنیم باید برای استفاده از کلاس‌ها یک سری محدودیت بگذاریم.

مثلا یک کلاس پایه داریم که این کلاس نباید توسط سایر کلاس ها مورد استفاده قرار بگیرد و تنها اعضای آن کلاس و کلاس هایی که از آن به ارث برده‌اند، توانایی دسترسی به متدها و اعضای آن را دارند یا مثلا کلاسی را ایجاد کرده‌ایم که با اعمال محدودیت‌هایی اجاره ایجاد کلاس فرزند از آن را نمی‌دهیم.

برای اعمال همچین محدودیت‌هایی از کلاس‌ها یا متدهایی با فرم abstract استفاده می‌شود.

کلاس‌ها و اعضاء abstract

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


public class Shape
{
    public void Draw()
    {
        Console.WriteLine("Drawing the shape!");
    }
}

public class Rectangle : Shape
{
         
}
 
public class Triangle : Shape
{
         
}
 
public class Circle : Shape
{
         
}

اگر به کلاس بالا دقت کنید، متوجه می‌شوید که کلاس Shape عملا برای ما کاربردی ندارد. به عبارت دیگر در طول برنامه‌ی اصلی از آن استفاده نشده است. یعنی باید محدودیتی اعمال کنیم که از روی کلاس Shape شیء‌ای ایجاد نشود. برای اینکار کافیست کلاس Shape را از نوع abstract تعریف کنیم. بنابراین طی یک تعریف کلی برای abstract‌ داریم:

اگر کلاسی به صورت abstract ایجاد شود، در طول برنامه نمی‌توان از روی آن شیءای ساخت.

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


public abstract class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing the shape!");
    }
}

حال اگر بخواهیم از روی کلاس Shape یک شیء ایجاد کنیم با خطای زیر مواجه می‌شویم:


Cannot create an instance of the abstract class

یعنی نمی‌توان از روی کلاس‌هایی که به صورت abstract‌ تعریف شده‌اند شیءای ایجاد کرد.

اما کاربردهای بیشتری از کلاس abstract انتظار می‌رود.

در کلاس‌های abstract‌ می‌توان به طور مشابه متدهایی را تعریف کرد که به صورت abstract‌ باشند. این متدها تنها شامل signature هستند یعنی بدنه‌ای نداشته و پس از تعریف آنها به علامت ; ختم می‌شوند.

یک متد به صورت abstract تعریف شود بدین گونه است که حتما باید آن را جهت استفاده در طی برنامه یا کلاس دیگر override کنند.

مثال یکبار دیگر کلاس Shape‌ را بازنویسی می‌کنیم:


public abstract class Shape
{
    public abstract void Draw();
}

همانطور که ملاحظه می‌کنید متدی به نام Draw‌ وجود دارد که بدنه‌ای ندارد. علت این امر تعریف این متد به صورت abstract است. حال اگر کلاس فرزندی از کلاس Draw به ارث ببرد باید همواره متد درون آن به صورت abstract قرار بگیرد. بنابراین داریم:


public class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing rectangle!");
    }
}

مروری بر abstrac class

• معمولاً از abstrac class در زمان ارثبری استفاده میشود.

• abstrac class هرگز قصد ندارد که به صورت مستقیم نمونه سازی شود.

• abstrac class معمولاً برای تعریف یک کلاس پایه در سلسله مراتب کلاسها استفاده میشوند.

• هر کلاس مشتق شده باید تعاریف abstrac class را پیاده سازی کند