שאלת מבחן בתכנות מונחה עצמים - אוניברסיטת בר-אילן 2014 - מחלקות
משחק ה-Arkanoid שפיתחנו במהלך הסמסטר תפס את תשומת ליבה של חברת פרסום גדולה, והם רוצים להשתמש בו כחלק מפתרונות הפרסום שלהם. ברצונם ליצור גרסה מותאמת של המשחק לכל אחד מהלקוחות: סידור בלוקים, ציורי בלוקים וציורי מסכים המתאימים למוצרי החברה. ברצונם לבצע את ההתאמות האלו על ידי המעצבים הגרפיים של החברה, ללא צורך בגישה לקוד של המשחק. למרבה השמחה יכולות אלו כבר נתמכות במשחק כפי שהוא קיים כיום. עם זאת, כדי לסגור את העסקה, הם מעוניינים ביכולת נוספת. הם היו רוצים להגדיר עבור חלק מהבלוקים את ההתנהגות הבאה: עבור חלק מהבלוקים, כל פגיעה בהם תגרור "באנר" שמכיל תמונה קבועה מראש שיזוז על המסך מצד שמאל לצד ימין במהירות מסוימת ובגובה מסוים. לכל בלוק ניתן יהיה להגדיר האם הוא גורר באנר או לא, ואם כן באיזו תמונה, איזו מהירות ואיזה גובה הוא יוצג. כמובן שגם ההגדרות האלו (איזה באנר, איזו מהירות, איזה גובה ואילו בלוקים יגררו את ההתנהגות הזו) יקבעו על ידי חברת הפרסום ללא גישה לקוד ה-Java של המשחק.
תארו את כל השינויים וההתאמות הנדרשים במשחק להוספת היכולת הזו. אין צורך לכתוב קוד, אך יש לכתוב תיאור מפורט של השינויים, כאילו הייתם מסבירים לגורם שלישי כיצד לכתוב את הקוד.
שימו לב: יש לשמור על עקרונות התכנות מונחה העצמים, והפרדת תחומי האחריות של מחלקות שונות. כמו כן, אסור לשנות את המימושים של המחלקה
תארו את כל השינויים וההתאמות הנדרשים במשחק להוספת היכולת הזו. אין צורך לכתוב קוד, אך יש לכתוב תיאור מפורט של השינויים, כאילו הייתם מסבירים לגורם שלישי כיצד לכתוב את הקוד.
שימו לב: יש לשמור על עקרונות התכנות מונחה העצמים, והפרדת תחומי האחריות של מחלקות שונות. כמו כן, אסור לשנות את המימושים של המחלקה
Block או מחלקות שמרחיבות או יורשות ממנה. בפרט, הבלוק לא אמור לדעת שהוא גורם לבאנר להופיע. ניתן לעבור על חוקים אלו, בעלות של חצי מהניקוד עבור השאלה.העתק שאלה
שתף שאלה
סמן כחשוב
סמן כבוצע
אוניברסיטת בר-אילןמועד ג · 2014 · סמסטר ב
★★★★★
מחלקותאובייקטיםממשקיםפולימורפיזםהסתרת מידעתבניות עיצוב
השתמשו במנגנון ה-
HitListener הקיים כדי להגיב לאירוע פגיעה בבלוק, מבלי לשנות את מחלקת ה-Block עצמה. יש ליצור HitListener חדש שיהיה אחראי על יצירת הבאנר.כדי להוסיף את היכולת המבוקשת תוך שמירה על עקרונות תכנות מונחה עצמים והמגבלות שהוצגו, יש לבצע את השינויים הבאים המבוססים על תבנית העיצוב Observer ו-תבנית העיצוב Factory.
1. יצירת מחלקה חדשה `Banner`:
2. יצירת מאזין חדש `BannerCreator`:
2. הוספת ה-
3. קונפיגורציה חיצונית ו-Factory ליצירת מאזינים:
4. שילוב בתהליך טעינת השלב:
2. לקרוא למתודה
3. אם המתודה החזירה אובייקט
באמצעות ארכיטקטורה זו, השגנו את כל המטרות: הוספנו את היכולת החדשה, ההגדרות שלה חיצוניות וניתנות לשינוי ללא הידור מחדש, ולא שינינו את מחלקת
1. יצירת מחלקה חדשה `Banner`:
- יש ליצור מחלקה חדשה
Bannerשתייצג את הבאנר הפרסומי. - מחלקה זו תממש את הממשק `Sprite`, כך שניתן יהיה להוסיף אותה לאוסף ה-Sprites של המשחק, והיא תצויר ותתעדכן בכל פעימה של לולאת המשחק.
- למחלקה יהיו התכונות הבאות: תמונה (
Image), מיקום (Point), ומהירות (Velocity). - הבנאי של המחלקה יקבל את נתיב התמונה, מהירות אופקית, וגובה (קואורדינטת y) קבועים. המיקום ההתחלתי יהיה בקצה השמאלי של המסך בגובה הנתון.
- המתודה
timePassedתעדכן את מיקום הבאנר בהתאם למהירות שלו. יש לממש לוגיקה שתסיר את הבאנר מהמשחק (באמצעות קריאה למתודה כמוgame.removeSprite(this)) כאשר הוא יוצא מגבולות המסך הימני. לשם כך, הבאנר יצטרך להחזיק רפרנס לאובייקט המשחק (GameLevel). - המתודה
drawOnתצייר את תמונת הבאנר במיקומו הנוכחי.
2. יצירת מאזין חדש `BannerCreator`:
- זהו לב הפתרון, המממש את תבנית ה-Observer. ניצור מחלקה חדשה בשם
BannerCreatorאשר תממש את הממשק `HitListener`. - מחלקה זו תהיה אחראית על יצירת אובייקט
Bannerוהוספתו למשחק כתגובה לאירוע פגיעה בבלוק. - הבנאי שלה יקבל רפרנס לאובייקט המשחק (
GameLevel) ואת המאפיינים הדרושים ליצירת הבאנר הספציפי (נתיב תמונה, מהירות וגובה). - המתודה
hitEvent(Block beingHit, Ball hitter)תכיל את הלוגיקה הבאה:
Banner באמצעות המאפיינים שנשמרו במאזין.2. הוספת ה-
Banner החדש לאוסף ה-Sprites של המשחק באמצעות קריאה ל-gameLevel.addSprite(newBanner). - בצורה זו, מחלקת
Blockאינה מודעת כלל לקיומם של באנרים. היא רק מבצעת את חובתה להודיע לכל המאזינים הרשומים לה על אירוע פגיעה. זהו יישום של עקרון הפרדת תחומי אחריות (Separation of Concerns).
3. קונפיגורציה חיצונית ו-Factory ליצירת מאזינים:
- כדי לאפשר הגדרה של הבאנרים ללא שינוי בקוד, ניצור קובץ הגדרות חיצוני (למשל
banner_definitions.txt). - קובץ זה ימפה בין סמל של בלוק (כפי שמופיע בקובץ הגדרת השלב) לבין מאפייני הבאנר שהוא אמור ליצור.
- לדוגמה, שורה בקובץ יכולה להיראות כך:
bdef symbol:X image:ads/banner.png speed:150 y:300 - ניצור מחלקה חדשה, `BannerFactory`, שתהיה אחראית על קריאת קובץ ההגדרות וייצור של אובייקטי
BannerCreatorמתאימים. - ה-Factory יקרא את הקובץ וישמור את המיפויים (למשל בתוך
Map). - הוא יספק מתודה כמו
createListener(String blockSymbol, GameLevel game)אשר בודקת אם לסמל הבלוק הנתון יש הגדרת באנר. אם כן, היא תיצור ותחזיר מופע חדש שלBannerCreatorעם המאפיינים המתאימים; אחרת, תחזירnull.
4. שילוב בתהליך טעינת השלב:
- יש לעדכן את הקוד שאחראי על טעינת שלב מקובץ.
- בתחילת טעינת השלב, יש ליצור מופע של
BannerFactoryולטעון אליו את קובץ הגדרות הבאנרים. - בתוך הלולאה שיוצרת את הבלוקים של השלב, עבור כל בלוק שנוצר, יש לבצע את הפעולות הבאות:
2. לקרוא למתודה
bannerFactory.createListener עם סמל הבלוק ורפרנס למשחק.3. אם המתודה החזירה אובייקט
HitListener (כלומר, לא null), יש לרשום אותו כמאזין של הבלוק שזה עתה נוצר באמצעות block.addHitListener(theNewListener).באמצעות ארכיטקטורה זו, השגנו את כל המטרות: הוספנו את היכולת החדשה, ההגדרות שלה חיצוניות וניתנות לשינוי ללא הידור מחדש, ולא שינינו את מחלקת
Block או את היורשות ממנה, תוך שמירה על עקרונות OOP חשובים.