Menu
Coddy logo textTech

פולימורפיזם באמצעות ממשקים

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Dart של Coddy. שיעור 58 מתוך 110.

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

כאשר מחלקות מממשות את אותו ממשק, אפשר להתייחס אליהן באופן פולימורפי גם אם אין ביניהן קשר של ירושה:

abstract class Printable {
  void printDetails();
}

class Invoice implements Printable {
  @override
  void printDetails() => print('Invoice #1234');
}

class Report implements Printable {
  @override
  void printDetails() => print('Monthly Report');
}

class Receipt implements Printable {
  @override
  void printDetails() => print('Receipt for purchase');
}

void printAll(List<Printable> items) {
  for (var item in items) {
    item.printDetails();
  }
}

void main() {
  var documents = [Invoice(), Report(), Receipt()];
  printAll(documents);
}

Invoice, Report ו־Receipt הן מחלקות שאינן קשורות כלל — אין להן מחלקת אב משותפת. ובכל זאת, מכיוון שכולן מממשות את Printable, אפשר לאחסן אותן באותה רשימה ולקרוא ל־printDetails() בכל אחת מהן.

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

challenge icon

אתגר

קל

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

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

  • media.dart: הגדירו כאן את הממשק ואת מחלקות המדיה:
    • מחלקה מופשטת Playable עם שתי מתודות מופשטות: play() ו-String getMediaType()
    • מחלקה Song שמממשת את Playable. צריכים להיות לה String title ו-String artist. המתודה שלה play() מדפיסה Playing song: [title] by [artist], והמתודה getMediaType() מחזירה 'Audio'
    • מחלקה Video שמממשת את Playable. צריכים להיות לה String title ו-int duration (בדקות). המתודה שלה play() מדפיסה Playing video: [title] ([duration] min), והמתודה getMediaType() מחזירה 'Video'
    • מחלקה Podcast שמממשת את Playable. צריכים להיות לה String title ו-int episode. המתודה שלה play() מדפיסה Playing podcast: [title] - Episode [episode], והמתודה getMediaType() מחזירה 'Podcast'
    צרו גם פונקציה playAll(List<Playable> playlist) שעוברת על הרשימה, ומדפיסה עבור כל פריט את [Media Type]: ואז קוראת למתודה play() של אותו פריט.
  • main.dart: ייבאו את קובץ המדיה והדגימו פולימורפיזם המבוסס על ממשק:
    • צרו List<Playable> שמכיל Song עם הכותרת 'Imagine' והאמן 'John Lennon', Video עם הכותרת 'Dart Tutorial' ומשך של 15, ו-Podcast עם הכותרת 'Tech Talk' ומספר פרק 42
    • העבירו את הרשימה הזו אל playAll()

שימו לב של-Song,‏ Video ו-Podcast אין מחלקת אב משותפת — אלה טיפוסים בלתי קשורים לחלוטין. ובכל זאת, מכיוון שכולם מממשים את Playable, אפשר לאחסן אותם באותה רשימה ולטפל בהם באופן אחיד באמצעות הממשק.

פלט צפוי:

Audio:
Playing song: Imagine by John Lennon
Video:
Playing video: Dart Tutorial (15 min)
Podcast:
Playing podcast: Tech Talk - Episode 42

נסו בעצמכם

import 'media.dart';

void main() {
  // TODO: צרו List<Playable> שמכילה:
  // - Song עם הכותרת 'Imagine' והאמן 'John Lennon'
  // - Video עם הכותרת 'Dart Tutorial' ומשך של 15
  // - Podcast עם הכותרת 'Tech Talk' ופרק 42
  
  // TODO: העבירו את הרשימה ל־playAll()
}
quiz iconבחנו את עצמכם

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

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

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