Menu
Coddy logo textTech

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

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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");
    }
}

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

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

השתמש בממשק כאשר מחלקות שאינן קשורות צריכות לחלוק התנהגות (כמו 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# אונליין