ראינו פעם שעברה מהו explicit implementation (מימוש פרטי) של ממשק.
אחד הדברים הנחמדים שחשבתי שאפשר לעשות עם מימוש פרטי הוא דבר כזה:
נניח שיש לנו יצור שמסוגל לנוע בכל כיוון (צפון, דרום, מזרח ומערב), אז נוכל ליצור ממשק כזה:
|
|
כעת ניצור שני ממשקים שיורשים ממנו:
|
|
וכעת ניצור מחלקה כזאת:
|
|
ונרצה לממש מימושים פרטיים של כל אחד מהממשקים:
|
|
אלא שנקבל למרבה הצער את השגיאות הבאות בזמן קימפול:
‘Creature’ does not implement interface member ‘IMoveable.MovePrevious()’
‘Creature’ does not implement interface member ‘IMoveable.MoveNext()’
‘IVerticalMoveable.MoveNext’ in explicit interface declaration is not a member of interface
‘IVerticalMoveable.MovePrevious’ in explicit interface declaration is not a member of interface
‘IHorizontalMoveable.MoveNext’ in explicit interface declaration is not a member of interface
‘IHorizontalMoveable.MovePrevious’ in explicit interface declaration is not a member of interface
למה זה קורה?
המתודות שיש בממשקים נמצאות בממשק הראשון (IMoveable). לכן הן שייכות לממשק הראשון. לכן כשנממש אותן בתור מימוש פרטי, נצטרך לציין שזהו מימוש של IMoveable.
בקיצור, מבאס.
עוד דוגמה:
הפעם דווקא לא מתחום הExplicit implementation.
נניח שיש לנו מחלקת אב שמממשת ממשק:
|
|
שימו לב שבמחלקת האב, הפונקציה Draw אינה וירטואלית.
עכשיו ברצוננו לרשת ממחלקת האב וליצור פונקציית Draw אחרת:
|
|
אלא שכעת כשנקרא לDraw עם IDrawable על Triangle, נקבל את התוצאה הלא רצויה הבאה:
|
|
מה שקורה זה תהליך מעט מורכב. המתודה Draw היא מתודה של ממשק ולכן מפוענחת בזמן ריצה, ולא בזמן קימפול.
מה שקורה זה בזמן שמקומפלת המחלקה Shape, מאחר והיא מממשת את IDrawable, מתבצע מיפוי של הפונקציה Draw של הממשק, למתודה שנמצאת אצל Shape.
ננסה אולי לעשות משהו כזה:
|
|
אבל נקבל לצערנו את שגיאת הקימפול הבאה:
‘Triangle.IDrawable.Draw()’: containing type does not implement interface ‘IDrawable’
כלומר, מימוש explicit של ממשק, לא עובר בירושה.
אז ננסה עוד משהו:
|
|
וכעת הקוד הבא יעבוד:
|
|
נראה שהבעיה שלנו נפתרה, אבל זה לא כך. אם נעשה משהו כזה:
|
|
שהרי קריאה על instance של Shape, קוראת למתודה Draw של Shape (המתודה לא וירטואלית, ולכן הקריאה מפוענחת בזמן קימפול), ולכן לא פותר לנו את הבעיה, אלא רק מחליף לנו אותה בבעיה אחרת.
המסקנה היא:
- כאשר אנחנו מעצבים מחלקת אב שמממשת ממשק, כדאי שנחשוב על להפוך את המתודות של הממשק לוירטואליות
- כאשר אנחנו יורשים ממחלקת אב שמממשת ממשק, בה המתודות של הממשק אינן וירטואליות, לא ננסה לעקוף את מגבלה זו ע"י מימוש הממשק מחדש, על מנת לצמצם צרות.
המשך יום ממומשק טוב