ממשק לעומת מחלקה מופשטת
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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# מודרנית מאפשרת מימושי ברירת מחדל.
כשיש ספק, העדיפו ממשקים לשם גמישות — תמיד תוכלו להוסיף מחלקה מופשטת בהמשך אם יהיה צורך במימוש משותף.
אתגר
קלבואו נבנה מערכת לניגון מדיה המדגימה מתי להשתמש בממשקים לעומת במחלקות מופשטות. תיצרו עיצוב שבו מכשירים לא קשורים חולקים התנהגות של ניגון באמצעות ממשק, בעוד שסוגי מדיה קשורים חולקים מימוש משותף באמצעות מחלקה מופשטת.
תארגנו את הקוד שלכם בארבעה קבצים:
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
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמרחבי שמות והנחיותמבוא למחלקות ולאובייקטיםמילת המפתח 'this'מתודות ופרמטריםשדות לעומת מאפייניםבנאיםאתחול אובייקטיםסיכום – מחשבון פשוט4ירושה
תחביר ירושה בסיסית (:)מילת המפתח 'base'מילות המפתח Virtual ו-Overrideמחלקות Sealedמחלקת הבסיס 'object'סיכום – היררכיית עובדים7תכונות מתקדמות
העמסת אופרטוריםאינדקסרים (this[])דריסת ToString()מתודות הרחבהסיכום – רשימה מותאמת אישית2מאפיינים וחברים סטטיים
מאפיינים במימוש אוטומטימאפיינים לקריאה בלבד ולכתיבה בלבדשדות ומתודות סטטייםמחלקות סטטיותחברים עם גוף ביטוי5פולימורפיזם וממשקים
פולימורפיזם בזמן קומפילציה לעומת בזמן ריצהממשק לעומת מחלקה מופשטתממשקים מרוביםממשקים מפורשיםהמרה כלפי מעלה והמרה כלפי מטהחזרה: מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה במקום ירושהGenerics (מחלקות ומתודות)נציגים ואירועיםמאפיינים ורפלקציהIDisposable והצהרת usingיסודות הזרקת תלויותתרגלו בעצמכם: קומפיילר C# אונליין