ערכות מול ירושה היא אחת משאלות הראיונות שנשאלות בתדירות. כמו כן, כנראה ששמעת גם על המלצה להשתמש בהרכבה במקום בירושה.
ערכות מול ירושה
שתי הערכות והירושה הם מושגים בתכנות מונחים. הם אינם קשורים לשפת תכנות ספציפית כמו גם לא יאבקו עם שפות תכנות כמו Java. לפני שנשווה באופן פרוגרמטי בין הרכבה לבין ירושה, בואו נקבע הגדרה מהירה שלהם.
הרכבה
הרכבה היא טכניקת עיצוב בתכנות מונחים למימוש יחס "יש לו" בין אובייקטים. ב-Java, הרכבה מתבצעת על ידי שימוש במשתנים מופע של אובייקטים אחרים. לדוגמה, אדם שיש לו עבודה מיושם כך בתכנות מונחים ב-Java.
package com.journaldev.composition;
public class Job {
// משתנים, שיטות וכו'
}
package com.journaldev.composition;
public class Person {
// יחס הרכבה יש לו
private Job job;
// משתנים, שיטות, בנאי וכו' בתכנות מונחים
ירושה
המורשת היא טכניקת עיצוב בתכנות מונחה עצמים ליישום זיקה בין אובייקטים. המורשת ב-Java מיושמת באמצעות המילת מפתח extends. לדוגמה, היחס בין חתול לבין חיה בתכנות ג'אווה ייושם כמפורט למטה.
package com.journaldev.inheritance;
public class Animal {
// משתנים, פעולות וכו'
}
package com.journaldev.inheritance;
public class Cat extends Animal{
}
הרכבה מעל למורשת
שתי הרכיבות והמורשת תומכים בשימוש מחדש בקוד דרך גישות שונות. אז איזו מהן לבחור? איך להשוות בין הרכבה לבין מורשת. כנראה ששמעת כבר שבתכנות עדיף להעדיף הרכבה מעל למורשת. בוא נכיר כמה מהסיבות שיעזרו לך לבחור בין הרכבה לבין מורשת.
-
ההמשכיות מחוברת באופן צמוד בעוד ההרכבה מחוברת באופן פתור. בואו נניח שיש לנו כיתות כאלה עם הורשה.
package com.journaldev.java.examples; public class ClassA { public void foo(){ } } class ClassB extends ClassA{ public void bar(){ } }
לצורך פשטות, נניח שהכיתה העל והתת כיתה נמצאות באותו חבילה. אך ברוב המקרים הן יהיו בקוד מקור נפרד. ייתכן שיהיו כמה כיתות המרחיבות את המחלקה העל. דוגמה נפוצה מאוד למצב כזה היא הרחבה של המחלקה Exception. עכשיו נניח שמימוש המחלקה ClassA השתנה כך, נוספה שיטה חדשה בשם bar().
package com.journaldev.java.examples; public class ClassA { public void foo(){ } public int bar(){ return 0; } }
כמעט מיידית כשתתחילו להשתמש במימוש החדש של ClassA, תקבלו שגיאת זמן קומפילציה ב-ClassB כגון
הסוג המוחזר אינו תואם את ClassA.bar()
. הפתרון יהיה לשנות את מימוש השיטה bar() של המחלקה העל או של המחלקה התת. אם הייתם משתמשים ברכיבה על פני הורשה, לא הייתם נתקלים בבעיה זו מעולם. דוגמה פשוטה למימוש של ClassB באמצעות רכיבה עשויה להיות כמטה.class ClassB{ ClassA classA = new ClassA(); public void bar(){ classA.foo(); classA.bar(); } }
-
אין שליטה בגישה בירושה כל עוד אפשר להגביל את הגישה בהרכבה. אנו חושפים את כל מתודות מחלקת העל למחלקות אחרות שיש להן גישה למחלקת המשנה. לכן, אם מתודה חדשה מוצגת או יש פרצות אבטחה במחלקת העל, מחלקת המשנה מתחשקת. מאחר שבהרכבה אנו בוחרים אילו מתודות להשתמש, היא מוגנת יותר מהירושה. לדוגמה, אנו יכולים לספק את מתודת ה-foo() של ClassA למחלקות אחרות באמצעות הקוד שלמטה ב-ClassB.
class ClassB { ClassA classA = new ClassA(); public void foo(){ classA.foo(); } public void bar(){ } }
זהו אחד היתרונות המרכזיים של הרכבה על פני ירושה.
-
הרכבה מספקת גמישות בקריאה למתודות שימושית בסצנריו של מספר תתי-מחלקות. לדוגמה, נניח שיש לנו את התרחשות הירושה הבאה.
abstract class Abs { abstract void foo(); } public class ClassA extends Abs{ public void foo(){ } } class ClassB extends Abs{ public void foo(){ } } class Test { ClassA a = new ClassA(); ClassB b = new ClassB(); public void test(){ a.foo(); b.foo(); } }
כך מה יקרה אם יש עוד תתי-מחלקות, האם הרכבה תגרום לקוד שלנו להיות כזה בלתי-נעים על ידי יצירת מופע אחד עבור כל תת-מחלקה? לא, נוכל לכתוב מחדש את מחלקת המבחן כמו שלמטה.
class Test { Abs obj = null; Test1(Abs o){ this.obj = o; } public void foo(){ this.obj.foo(); } }
זה יאפשר לך להשתמש בגמישות בכל תת-מחלקה על פי האובייקט שנמצא בבנאי.
-
עוד יתרון של הרכבה מול ירושה הוא טווח הבדיקה. בדיקת יחידות פשוטה בהרכבה מכיוון שאנו יודעים אילו שיטות אנו משתמשים ממחלקה אחרת. אנו יכולים להפעיל בדיקות על כך בקלות, כאשר בירושה אנו תלויים בצורה משמעית במחלקת-האם ולא יודעים אילו שיטות ממחלקת-האם יועשה שימוש בהם. אז נצטרך לבדוק את כל השיטות של מחלקת-האם. זה עבודה נוספת ולא נצטרך לעשות אותה בשוב עקבי ליירוש.
זהו, זה כל המדריך על הרכבה נגד ירושה. יש לך מספיק סיבות לבחור בהרכבה על פני ירושה. השתמש בירושה רק כאשר אתה בטוח שמחלקת-ההורה לא תשתנה, אחרת עדיף לך על הרכבה.
Source:
https://www.digitalocean.com/community/tutorials/composition-vs-inheritance