Abstract Class vs Interface
ההבדל המרכזי בין abstract class לבין interface מתבטא במטרה התכנונית שלהם: abstract class מייצגת קשר מסוג IS-A: משמש ליצירת היררכיה ומשפחה. למשל, Dog הוא Animal. הם חולקים DNA משותף (קוד, שדות, לוגיקה). אם יש לנו קוד אמיתי שאנחנו רוצים שכל הילדים יירשו קומפלט, מחלקה אבסטרקטית היא הפתרון.
בעוד שinterface מייצג קשר מסוג CAN-DO / HAS-A Capability משמש להוספת יכולות חוצות משפחות. למשל, Car יכול להיות Flyable וגם Electric, וגם Duck יכול להיות Flyable. אין קשר משפחתי בין מכונית לברווז, אבל לשניהם יש את היכולת לעוף. ממשקים מאפשרים גמישות מוחלטת שלא תלויה בהיררכיית הירושה.
יתרונות וחסרונות
Abstract Class
יתרונות:
- מאפשר שיתוף קוד ממשי (Code Reuse) וחסכון בכתיבת קוד כפול באמצעות מתודות מוכנות מראש.
- מאפשר לשמור מצב פנימי (State) באמצעות שדות (Fields) וקונסטרוקטורים משותפים.
חסרונות:
- מגביל את התכנון הודות למגבלת הירושה הבודדת (Single Inheritance)- מחלקה לא יכולה לרשת מחלקה אבסטרקטית אחרת אם היא כבר יורשת מחלקה אחת.
Interface
יתרונות:
- מעניק גמישות מרבית הודות לתמיכה בריבוי ממשקים (Multiple Inheritance of Type), מה שמאפשר למחלקה "לאמץ" כמה תפקידים שונים.
- יוצר הפרדה מוחלטת (Decoupling) בין החוזה לבין המימוש בפועל.
חסרונות:
- אינו מאפשר שיתוף של משתני מופע או קונסטרוקטורים, מה שיכול לדרוש כתיבת קוד חזרתי במידה ואין שימוש במתודות ברירת מחדל (default).
השוואה בין Abstract Class ל-Interface
| תכונה | Abstract Class | Interface |
|---|---|---|
| ירושה (Inheritance) | תומכת בירושה בודדת בלבד (מחלקת-בת יכולה לרשת מחלקה אבסטרקטיבית אחת). | תומכת בריבוי ממשקים (מחלקה יכולה לממש כמה ממשקים במקביל). |
| מצב (State / Fields) | יכולה להכיל משתני מופע (Fields) עם כל רמת נגישות (private, protected, public). | לרוב מכילה רק קבועים (public static final). לא יכולה להחזיק משתני מצב משתנים (Instance fields). |
| קונסטרוקטורים | יכולה להכיל קונסטרוקטור שמופעל באמצעות super() מתוך מחלקות הבת. | אין לה קונסטרוקטורים כלל (אי אפשר לאתחל אותה ישירות לא ניתן לקרוא ל NEW). |
| סוגי מתודות | יכולה להכיל מתודות אבסטרקטיות (ללא גוף) וגם מתודות מוגמרות עם מימוש מלא. | באופן מסורתי הכילה רק חתימות מתודות; החל מגרסאות מתקדמות (כמו Java 8 ) מאפשרת מתודות ברירת מחדל (default) ומתודות סטטיות. |
| הוספת מתודה | אם נרצה להוסיף מתודה רגילה (עם גוף) ל-Abstract Class בעתיד, נוכל לעשות זאת בקלות וכל מחלקות הבת יקבלו אותה מיד בלי שום בעיה. | הוספת מתודה לInterface מסוכנת יותר (או דרשה שינויים): היסטורית, אם הוספת מתודה ל-Interface, כל המחלקות שממשות אותו נשברו בקומפילציה כי הן חייבות לממש גם אותה. לכן הציגו ב-Java 8 את ה-default methods כדי לאפשר הוספת מתודות לממשקים קיימים בלי לשבור את הקוד הישן. |
מתי לבחור במה? (כלל אצבע פרקטי)
בחרנו ב-Interface כש:
- נרצה להגדיר חוזה משותף למחלקות שאין ביניהן קשר משפחתי.
- נצטרך תמיכה בריבוי ירושות (Multiple Inheritance), כלומר שטיפוס יוכל לממש מספר התנהגויות שונות.
- נרצה ליצור הפרדה מוחלטת (Decoupling) בין ההגדרה למימוש.
בחרנו ב-Abstract Class כש:
- נרצה לשתף קוד אמיתי (מתודות עם מימוש מלא) בין מחלקות קרובות.
- נצטרך לשמור מצב פנימי (Fields עם ערכים משתנים כמו name או status) ולנהל קונסטרוקטורים משותפים.
- המחלקות שלנו חולקות זהות משותפת חזקה (היררכיה ברורה).