Menu
CoddyTech

ממשק לעומת מחלקה מופשטת

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C# של Coddy. שיעור 26 מתוך 70.

גם ממשקים וגם מחלקות מופשטות מאפשרים לך להגדיר חוזים שמחלקות אחרות חייבות לפעול לפיהם, אך הם משמשים למטרות שונות ובעלי יכולות שונות.

ממשק מגדיר מה מחלקה חייבת לעשות, בלי לממש זאת. זהו חוזה טהור:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("Flapping wings");
    }
}

מחלקה אבסטרקטית יכולה לספק גם חוזה וגם מימוש משותף. היא מהווה תוכנית מתאר חלקית:

public abstract class Vehicle
{
    public string Brand { get; set; }
    
    public void StartEngine()
    {
        Console.WriteLine("Engine started");
    }
    
    public abstract void Drive();  // חובה לממש
}

public class Car : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving on roads");
    }
}

ההבדלים העיקריים נובעים מכללי הירושה ומהמטרה.

מחלקה יכולה לממש כמה ממשקים, אך לרשת רק מחלקה מופשטת אחת.

מחלקה יכולה גם לרשת מחלקה מופשטת ולממש ממשקים בו־זמנית: אחרי הנקודתיים מופיעה תחילה מחלקת הבסיס, ואחריה הממשקים, כשהם מופרדים בפסיקים:

public class Airplane : Vehicle, IFlyable
{
    public override void Drive()
    {
        Console.WriteLine("Taxiing to the runway");
    }

    public void Fly()
    {
        Console.WriteLine("Flying above the clouds");
    }
}

השתמשו בממשק כשמחלקות שאינן קשורות צריכות לחלוק התנהגות (כמו IFlyable עבור ציפורים ומטוסים). השתמשו במחלקה מופשטת כשמחלקות קשורות חולקות קוד ומצב משותפים (כמו כלי רכב שחולקים מנגנון התנעת מנוע).

למחלקות מופשטות יכולים להיות בנאים, שדות ומגבילי גישה לחברים. באופן מסורתי, לממשקים לא היו כאלה, אם כי C# מודרנית מאפשרת מימושי ברירת מחדל.

כשיש ספק, העדף ממשקים לשם גמישות — תמיד תוכל להוסיף מחלקה מופשטת בהמשך אם יהיה צורך במימוש משותף.

challenge icon

אתגר

קל

בואו נבנה מערכת של נגן מדיה שמדגימה מתי להשתמש בממשקים לעומת מחלקות מופשטות. תיצרו תכנון שבו מכשירים שאינם קשורים חולקים התנהגות הפעלה באמצעות ממשק, בעוד שסוגי מדיה קשורים חולקים מימוש משותף באמצעות מחלקה מופשטת.

תארגנו את הקוד שלכם בארבעה קבצים:

  • IPlayable.cs: הגדירו ממשק בשם IPlayable במרחב השמות Media. ממשק זה מייצג את החוזה עבור כל דבר שיכול להפעיל מדיה — בין אם זה טלפון, טלוויזיה או נגן מוזיקה ייעודי. עליו להצהיר על מתודה יחידה Play() שמחזירה מחרוזת.
  • MediaFile.cs: הגדירו מחלקה מופשטת בשם MediaFile במרחב השמות Media. היא מייצגת את התשתית המשותפת לכל קובצי המדיה. עליה לכלול מאפיין Title (מחרוזת) ומאפיין Duration (מספר שלם, המייצג שניות). כללו בנאי שמגדיר את שני הערכים. הוסיפו מתודה קונקרטית בשם GetInfo() שמחזירה "{Title} ({Duration}s)". הצהירו גם על מתודה מופשטת GetType() שמחזירה מחרוזת — כל סוג מדיה יגדיר איזה סוג קובץ הוא.
  • AudioFile.cs: הגדירו מחלקה בשם AudioFile במרחב השמות Media, שיורשת מ־MediaFile ומממשת את IPlayable. הוסיפו מאפיין Artist (מחרוזת). הבנאי אמור לקבל כותרת, משך זמן ואמן. דרסו את GetType() כך שתחזיר "Audio". ממשו את Play() כך שתחזיר "Playing audio: {Title} by {Artist}".
  • Program.cs: בקובץ הראשי שלכם, צרו AudioFile באמצעות ערכי הקלט. הדגימו את שני ההיבטים של המחלקה: קראו ל־GetInfo() ול־GetType() (שירשנו מהמחלקה המופשטת), ואז קראו ל־Play() (מהממשק). אחסנו את קובץ השמע במשתנה מסוג IPlayable וקראו שוב ל־Play() כדי להדגים פולימורפיזם באמצעות ממשק.

תקבלו שלושה ערכי קלט:

  • שם השיר
  • משך הזמן בשניות
  • שם האמן

הדפיסו את הפלט בפורמט הבא:

Info: {GetInfo() result}
Type: {GetType() result}
Direct: {Play() result}
Via Interface: {Play() result from IPlayable variable}

לדוגמה, אם ערכי הקלט הם Bohemian Rhapsody, 354 ו־Queen, הפלט אמור להיות:

Info: Bohemian Rhapsody (354s)
Type: Audio
Direct: Playing audio: Bohemian Rhapsody by Queen
Via Interface: Playing audio: Bohemian Rhapsody by Queen

שימו לב איך AudioFile נהנית משתי הגישות: היא יורשת מימוש משותף (GetInfo()) מהמחלקה המופשטת, ובמקביל מקיימת את החוזה של IPlayable. המחלקה המופשטת מאפשרת שימוש חוזר בקוד עבור סוגי מדיה קשורים, ואילו הממשק מאפשר לכל מחלקה שאינה קשורה להיות "ניתנת להפעלה" בלי לשתף היררכיית ירושה.

נסו בעצמכם

using System;
using Media;

class Program
{
    public static void Main(string[] args)
    {
        // קרא את ערכי הקלט
        string title = Console.ReadLine();
        int duration = Convert.ToInt32(Console.ReadLine());
        string artist = Console.ReadLine();
        
        // TODO: צור AudioFile באמצעות ערכי הקלט
        
        // TODO: הדפס Info: ואחריו את התוצאה של GetInfo()
        
        // TODO: הדפס Type: ואחריו את התוצאה של GetType()
        
        // TODO: הדפס Direct: ואחריו את התוצאה של Play()
        
        // TODO: אחסן את קובץ השמע במשתנה מסוג IPlayable
        
        // TODO: הדפס Via Interface: ואחריו את התוצאה של Play() מהמשתנה מסוג IPlayable
    }
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר C# אונליין