שאלת מבחן בתכנות מונחה עצמים - אוניברסיטת בר-אילן 2021 - ממשקים
שאלה 15. מספר במערכת 472089
(Biterator) תזכורת: בספריה הסטנדרטית בג'אווה מוגדר ממשק
כעת נרצה לתמוך במבני נתונים המאפשרים להתקדם לפי סדר האיברים אבל גם לחזור אחורה. לפי עקרונות התכנות שלמדנו, מה מבין הבאים הוא הפתרון הטוב ביותר לכך?
1. נכתוב ממשק
2. נכתוב ממשק
3. נכתוב ממשק
4. נכתוב ממשק
(Biterator) תזכורת: בספריה הסטנדרטית בג'אווה מוגדר ממשק
Iterable, המייצג מבנה נתונים שניתן לעבור עליו לפי סדר מסוים, וממשק Iterator אשר מייצג מעין מצביע המתקדם על איברי Iterable מסוים לפי סדר נתון.כעת נרצה לתמוך במבני נתונים המאפשרים להתקדם לפי סדר האיברים אבל גם לחזור אחורה. לפי עקרונות התכנות שלמדנו, מה מבין הבאים הוא הפתרון הטוב ביותר לכך?
1. נכתוב ממשק
Biterator עם מתודות next, hasNext, prev, hasPrev וכדומה, וממשק Biterable עם מתודה biterator אשר מחזירה ערך מטיפוס Biterator ועם מתודה iterator אשר מחזירה ערך מטיפוס Iterator2. נכתוב ממשק
Biterator אשר מרחיב את ממשק Iterator ומוסיף לו מתודות prev, hasPrev וכדומה, וממשק Biterable עם מתודה יחידה biterator אשר מחזירה ערך מטיפוס Biterator3. נכתוב ממשק
Biterator עם מתודות next, hasNext, prev, hasPrev וכדומה, וממשק Biterable המרחיב את ממשק Iterable ומוסיף לו מתודה biterator אשר מחזירה ערך מטיפוס Biterator4. נכתוב ממשק
Biterator אשר מרחיב את ממשק Iterator ומוסיף לו מתודות prev, hasPrev וכדומה, וממשק Biterable המרחיב את ממשק Iterable ו"ידרוס" את ההגדרה של iterator כך שתחזיר ערך מטיפוס Biteratorהעתק שאלה
שתף שאלה
סמן כחשוב
סמן כבוצע
אוניברסיטת בר-אילןמועד א2021סמסטר ב
★★★★★
ממשקיםירושהפולימורפיזםOverridingאיטרטורים
חשבו על יחס "is-a". האם איטרטור דו-כיווני הוא סוג של איטרטור? האם מבנה נתונים שניתן לעבור עליו דו-כיוונית הוא סוג של מבנה שניתן לעבור עליו? השתמשו בירושה כדי למדל יחסים אלו.
הפתרון הטוב ביותר הוא אפשרות 4. ננתח מדוע פתרון זה עדיף על פני האחרים על פי עקרונות תכנות מונחה עצמים:
1. ירושה (Inheritance): העיקרון המרכזי כאן הוא יחס "is-a". איטרטור דו-כיווני (
*
*
2. דריסת מתודות עם טיפוס החזרה קו-וריאנטי (Covariant Return Type): במקום להוסיף מתודה חדשה,
עיצוב זה חזק וחסכוני. הוא נמנע מכפילות (אין צורך ב-
3. ניתוח החלופות:
* אפשרויות 1 ו-3 שגויות מכיוון ש-
* אפשרויות 1 ו-2 שגויות מכיוון ש-
לסיכום, אפשרות 4 משתמשת נכון בירושה, פולימורפיזם ודריסת מתודות כדי ליצור פתרון גמיש, חזק ועקבי עם עקרונות התכנות ועם הממשקים הקיימים בספרייה הסטנדרטית.
1. ירושה (Inheritance): העיקרון המרכזי כאן הוא יחס "is-a". איטרטור דו-כיווני (
Biterator) הוא סוג של איטרטור (Iterator), ומבנה נתונים שניתן לעבור עליו דו-כיוונית (Biterable) הוא סוג של מבנה נתונים שניתן לעבור עליו (Iterable). הדרך הנכונה לממש יחס כזה היא באמצעות ירושה של ממשקים.*
public interface Biterator<T> extends Iterator<T>: הגדרה זו מבטיחה שכל Biterator הוא גם Iterator. כך, ניתן להשתמש ב-Biterator בכל מקום שמצפה ל-Iterator. זהו ביטוי של פולימורפיזם.*
public interface Biterable<T> extends Iterable<T>: הגדרה זו מבטיחה שכל מבנה נתונים Biterable הוא גם Iterable. זה מאפשר, למשל, להשתמש בו ישירות בלולאת for-each של ג'אווה, אשר עובדת על כל אובייקט המממש Iterable.2. דריסת מתודות עם טיפוס החזרה קו-וריאנטי (Covariant Return Type): במקום להוסיף מתודה חדשה,
biterator(), הפתרון האלגנטי ביותר הוא לדרוס (override) את המתודה הקיימת iterator() מהממשק Iterable. החל מג'אווה 5, ניתן לדרוס מתודה ולהחזיר טיפוס שהוא תת-טיפוס של טיפוס ההחזרה המקורי. מכיוון ש-Biterator יורש מ-Iterator, הוא תת-טיפוס שלו. לכן, הממשק Biterable יכול להכריז על המתודה iterator() כך שתחזיר Biterator.עיצוב זה חזק וחסכוני. הוא נמנע מכפילות (אין צורך ב-
biterator()) ושומר על חוזה ברור: כל Biterable מספק איטרטור, ואנו יודעים שהאיטרטור הזה הוא ספציפית Biterator המאפשר תנועה אחורה.3. ניתוח החלופות:
* אפשרויות 1 ו-3 שגויות מכיוון ש-
Biterator אינו יורש מ-Iterator, ובכך שובר את הפולימורפיזם.* אפשרויות 1 ו-2 שגויות מכיוון ש-
Biterable אינו יורש מ-Iterable, ובכך מונע שימוש במבני הנתונים עם מנגנונים סטנדרטיים של השפה כמו לולאת for-each.לסיכום, אפשרות 4 משתמשת נכון בירושה, פולימורפיזם ודריסת מתודות כדי ליצור פתרון גמיש, חזק ועקבי עם עקרונות התכנות ועם הממשקים הקיימים בספרייה הסטנדרטית.