חלקי קוד מעניינים של רב השלכתיות
כמו מקודם, תחילה נגדיר ממשק אשר יצור סוג חדש ויכריז על פונקציה כללית בה ניתן להשתמש על
מנת לקרוא בחזרה לכל אובייקט שהוא ממחלקה המיישמת את הממשק. אין כאן דבר חדש.
interface CallBack{ |
אחר כך נצטרך מחלקה שהאובייקטים שלה מחזיקים רשימה של אובייקטים רשומים מסוג CallBack (אובייקטים שהמחלקה שלהם מיישמת ממשק CallBack) ויכולים להודיע לכל האובייקטים על רשימה זו כאשר משהו מעניין קורה. כמו מקודם, נכנה מחלקה זו מורה.
מחלקה זו הגיעה לנקודה בה נפרק אותה לחלקים ונדון בהם בנפרד.
יש כאן יחסית הרבה דברים חדשים, עקב הדרישה לעיבוד רשימה. אין דברים חדשים בנוגע למכניזם הקריאה לאחור.
נתחיל על ידי החלפת המשתנה לאובייקט היחיד מסוג CallBback בפוינטר לאובייקט מסוג וקטור. נחזיק את הרשימה שלנו באובייקט זה. תיזכר, שאובייקט וקטור יכול רק לעבוד עם פוינטרים לסוג "אובייקט" כך שיש צורך בהשלכה למטה בהמשך.
הבנאי למחלקת המורה החדשה שלנו מאתחל אובייקט וקטור.
class Teacher{ Teacher(){//constructor |
אחר כך אנחנו זקוקים לפונקציה המוסיפה אובייקטים לרשימה. נתאם זאת כך שיגן מפני אפשרות של שניים או שלושה אובייקטים על מחרוזות שונות המנסים להירשם בו זמנית.
שים לב, שהפוינטרים לאובייקטים מתקבלים כסוג CallBack, שזהו סוג הממשק, ומאוחסנים כסוג "אובייקט" מכיוון שמחלקת וקטור יכולה להכיל רק פוינטרים לאובייקטים מסוג "אובייקט".
שוב הדבר יוביל לדרישת השלכה מטה בהמשך.
synchronized void register(CallBack obj){ |
באופן כללי, אנחנו גם זקוקים לפונקציה אשר תוריד אובייקטים מהרשימה. הורדת אובייקט מהרשימה היא מורכבת קצת יותר מהוספת אובייקט לרשימה עקב אפשרות שיהיו שני אובייקטים זהים או יותר על הרשימה (אנחנו יכולים וצריכים להגן מפני אפשרות זאת בעת בניית הרשימה).
הקטע הבא הלקוח מתוך תיעוד JDK 1.1.3 מתאר את פונקצית removeElement() של מחלקת וקטור בה אנו משתמשים על מנת לעשות זאת (שלוש פונקציות שונות קיימות לשם הורדת אובייקטים מוקטור).
public final synchronized boolean removeElement(Object obj)
This method removes the first occurrence of the argument from this vector. Indices beyond that point are adjusted appropriately Parameters: obj - the component to be removed. Returns: true if the argument was a component of this vector; false otherwise. |
עקב מתן הגדרה זו, הקוד להורדת אובייקט מהרשימה הוא ישיר.
synchronized void unRegister(CallBack obj){
if(this.objList.removeElement(obj))
System.out.println(obj + " removed");
else System.out.println(obj + " not in the list");
}//end register()
עכשיו אנו זקוקים לפונקציה על מנת לידע את כל האובייקטים ברשימה שמשהו מעניין קרה.
נכנה פונקציה זו callTheRoll() על מנת לדבוק בדוגמת הכתה שלנו.
אחת הבעיות הפוטנציאליות של מכניזם של קריאה לאחור מסוג זה היא שכאשר פונקצית קריאה לאחור מעוררת על אובייקט, ייתכן וייקח זמן עד שהיא תסתיים. (בזמן כתיבת פונקציות קריאה לאחור, אם הן עושות דבר מה משמעותי, הקוד בפונקציה צריך להביא עמו מחרוזת נוספת אשר תבצע את העבודה כך שיכול לחזור במהרה).
הדבר מוביל לאפשרות שאובייקטים נוספים ינסו להירשם בזמן הפסקה זו. על מנת להגן מפני אפשרות זו, נעשה עותק של מצב אובייקט הרשימה כפי שהתקיים בנקודה בה הוחלט לבצע את הקריאות חזרה, ואז נבצע את הקריאות חזרה תוך שימוש בעותק. בדרך זו, הרשימה המקורית זמינה לעדכונים כפי שנדרש בזמן הפסקה זו.
לאחר מכן, נשתמש בלולאת for על מנת לגשת לכל האובייקטים ברשימה ולעורר פונקצית callBack() על אובייקטים אלה (למעשה, הרשימה מכילה פוינטרים לאובייקטים, ולא את האובייקטים עצמם, כך שאנו מעוררים את הפונקציה על הפוינטרים).
כפי שהובטח קודם, עלינו להשליך מטה מאובייקט ל-CallBack על מנת לזכות בגישה לפונקצית callBack() בכל האובייקטים.
void callTheRoll(){ for(int cnt = 0; cnt < tempList.size(); cnt++){ |
זה מסיים את הדיון במחלקה בשם מורה ומוביל אותנו למחלקה בשם תלמיד אשר מיישמת את ממשק CallBack. מחלקה זו לא השתנתה. כפי שצוין קודם, הגרסה של התוכנית היא גם בעלת מחלקה בשם כלב אשר מיישמת את הממשק. שתי מחלקות אלה הן באופן עקרוני זהות.
עקב הדמיון ביניהן, ומכיוון שהן למעשה זהות לתוכנית הקודמת, פשוט נראה את מחלקת כלב ללא דיון נוסף.
class Dog implements CallBack{ Dog(String name){ //An object of the Teacher class will invoke this method |
הדבר מוביל אותנו למחלקת שליטה המקשרת בין כל החלקים ומתרגלת אותם. גרסה זו של התוכנית שונה המגרסה הקודמת בעיקר במונחים של גודל האובייקטים תלמיד וכלב שיש לאתחלם ולרשומם באובייקט מורה. ישנן הרבה פקודות תצוגה העוזרות לנו לעקוב אחר המתרחש.
היכולת להוריד אובייקטים מהרשימה גם היא מוצגת.
לבסוף, הקריאה לאחור לאובייקטים ברשימה מתבצעת על ידי עוררות פונקצית callTheRoll()
על אובייקט מורה בשם missJones. הפלט מהרצת התוכנית ניתן אחרי רישום חלק הקוד.
נקודה דקה אך חשובה מאד מוצגת כאן. תלמיד וכלב הן מחלקות שונות. אובייקטים משתי המחלקות רשומים על גבי אובייקט יחיד ממחלקת מורה. לא חשוב לאובייקט המורה שהם שונים, כל עוד הם מאותחלים ממחלקה המיישמת את ממשק CallBack פונקצית register() תקבל רק פוינטרים לאובייקטים מסוג CallBack.
class Callback02{ //Instantiate some Student objects //Instantiate some Dog objects. //Register some Student and Dog objects with the //Remove a Student object from the list. //Try to remove an object that is not on the list. |
הפלט מהרצת התוכנית מוצגת להלן. תוכל לראות את זיהוי כל אובייקט אינדיבידואלי כאשר הוא מתווסף או מורד מהרשימה.
שים לב שהניסיון להוריד את Joe מהרשימה לא עלה יפה מכיוון שהוא לא נרשם מלכתחליה.
בסופו של דבר, תראה את הפלט המופק מעוררות callTheRoll() אשר בתורה מעוררת את פונקצית callBback() על כל אחד מהאובייקטים ברשימה.
שים לב ש-Peg לא הופיע ב-rollcall שכן הוא נוסף ראשון ואז הורד מהרשימה לפני שהתבצע
ה-rollcall…
Register Tom |
וזהו זה, הסכום והתוכן של קריאה לאחור רב השלכתית ב-Java. מן הסתם ניתן לבצע שיפורים. תראה חלק מהם בשני השיעורים הבאים.