ירושה והרכבה (Is-a לעומת Has-a relationship) בג'אווה

1. סקירה כללית

ירושה וקומפוזיציה - יחד עם הפשטה, אנקפסולציה ופולימורפיזם - הם אבני היסוד של תכנות מונחה עצמים (OOP).

במדריך זה נסקור את יסודות הירושה וההרכב, ונתמקד בחיזוק ההבדלים בין שני סוגי היחסים.

2. יסודות הירושה

ירושה היא מנגנון רב עוצמה אך מנוצל יתר על המידה.

במילים פשוטות, עם ירושה, מחלקת בסיס (aka סוג בסיס) מגדירה את המצב וההתנהגות המשותפים לסוג מסוים ומאפשרת לסיווגי המשנה (aka תת-סוגים) לספק גרסאות מיוחדות של אותו מצב והתנהגות.

כדי לקבל מושג ברור כיצד לעבוד עם ירושה, בואו ליצור דוגמה נאיבית: מעמד בסיס אדם המגדיר את השדות והשיטות המשותפים לאדם, ואילו תת הקטגוריות מֶלְצָרִית ו שַׂחְקָנִית לספק יישומים נוספים בשיטה גרגרית.

הנה ה אדם מעמד:

אדם בכיתה ציבורית {פרטי סופי שם מחרוזת; // שדות אחרים, קונסטרוקטורים סטנדרטיים, גטרים}

ואלה מחלקות המשנה:

מעמד ציבורי מלצרית מרחיבה את האדם {public String serveStarter (מחרוזת מחרוזת) {חזרה "מגישים" + סטרטר; } // שיטות / בונים נוספים} 
מעמד ציבורי שחקנית מרחיבה את האדם {public String readScript (סרט מחרוזת) {חזרה "קריאת התסריט של" + סרט; } // שיטות / בונים נוספים}

בנוסף, בואו ליצור בדיקת יחידה כדי לוודא שמופעים של ה- מֶלְצָרִית ו שַׂחְקָנִית שיעורים הם גם מקרים של אדם, ובכך מראה כי התנאי "is-a" מתקיים ברמת הסוג:

@Test הציבור בטל שניתןWaitressInstance_whenCheckedType_thenIsInstanceOfPerson () {assertThat (מלצרית חדשה ("מרי", "[מוגן באמצעות דוא"ל], 22)) .isInstanceOf (Person.class); } @Test הציבור בטל givenActressInstance_whenCheckedType_thenIsInstanceOfPerson () {assertThat (השחקנית החדשה ("סוזן", "[מוגן באמצעות דוא"ל], 30)) .isInstanceOf (Person.class); }

חשוב להדגיש כאן את הפן הסמנטי של הירושה. מלבד שימוש חוזר ביישום ה- כיתת אדם, יצרנו מערכת יחסים מוגדרת היטב של "זה- a" בין סוג הבסיס אדם ותתי הסוגים מֶלְצָרִית ו שַׂחְקָנִית. מלצריות ושחקניות הן למעשה אנשים.

זה עשוי לגרום לנו לשאול: באילו מקרי שימוש הירושה היא הגישה הנכונה לנקוט?

אם תת-סוגים ממלאים את התנאי "is-a" ובעיקר מספקים פונקציונליות תוספים בהמשך ההיררכיה של הכיתות,ואז הירושה היא הדרך ללכת.

כמובן שעקיפת שיטות מותרת כל עוד השיטות העוקפות שומרות על תחליפי סוג הבסיס / תת-סוג שמקודמת על ידי עקרון ההחלפה של ליסקוב.

בנוסף, עלינו לזכור זאת תת-הסוגים יורשים את ה- API של סוג הבסיס, שמקרים מסוימים עשויים להיות מוגזמים או פשוט לא רצויים.

אחרת, עלינו להשתמש בקומפוזיציה במקום.

3. ירושה בדפוסי עיצוב

בעוד שהקונצנזוס הוא שעדיף להרכב על פני ירושה במידת האפשר, ישנם כמה מקרי שימוש אופייניים בהם הירושה מקומה.

3.1. תבנית העל שכבת העל

במקרה זה אנו השתמש בירושה כדי להעביר קוד משותף למחלקת בסיס (סוג העל), על בסיס שכבה.

להלן יישום בסיסי של דפוס זה בשכבת התחום:

ישות בכיתה ציבורית {מוגנת מזהה ארוך; // סטרים} 
מחלקה ציבורית משתמש מרחיב את הישות {// שדות ושיטות נוספות} 

אנו יכולים ליישם את אותה גישה על השכבות האחרות במערכת, כגון שכבות השירות והתמדה.

3.2. תבנית שיטת התבנית

בתבנית שיטת התבנית אנו יכולים השתמש בכיתת בסיס כדי להגדיר את החלקים הבלתי משתנים של אלגוריתם, ואז יישם את החלקים הגרסאיים בקטגוריות המשנה:

מחלקה מופשטת ציבורית ComputerBuilder {public final מחשב buildComputer () {addProcessor (); addMemory (); } תקציר ריק ריק addProcessor (); תקציר פומבי ריק ריק AddMemory (); } 
מחלקה ציבורית StandardComputerBuilder מרחיב את ComputerBuilder {@Override public void addProcessor () {// method implement}} @Override public void addMemory () {// method method}}

4. יסודות הקומפוזיציה

ההרכב הוא מנגנון נוסף שמספק OOP לשימוש חוזר ביישום.

בְּקִצוּר נִמרָץ, קומפוזיציה מאפשרת לנו לדגמן אובייקטים המורכבים מאובייקטים אחרים, ובכך מגדירים יחסי "יש- a" ביניהם.

יתר על כן, ההרכב הוא צורת ההתאגדות החזקה ביותר, מה שאומר ש האובייקט (ים) המרכיבים או מכילים עצם אחד נהרסים גם הם כאשר אובייקט זה נהרס.

כדי להבין טוב יותר כיצד קומפוזיציה עובדת, נניח כי עלינו לעבוד עם אובייקטים המייצגים מחשבים.

מחשב מורכב מחלקים שונים, כולל המיקרו-מעבד, הזיכרון, כרטיס קול וכן הלאה, כך שנוכל לדגם את המחשב ואת כל אחד מחלקיו כשיעורים בודדים.

כך יישום פשוט של ה- מַחשֵׁב הכיתה עשויה להיראות:

מחשב בכיתה ציבורית {מעבד מעבד פרטי; זיכרון זיכרון פרטי; פרטי SoundCard SoundCard; // getters / סטרים סטנדרטיים / בונים ציבוריים getSoundCard () {return Optional.ofNullable (soundCard) אופציונלי; }}

הכיתות הבאות מדגמנות מעבד, זיכרון וכרטיס קול (ממשקים הושמטו לשם קיצור):

מחלקה ציבורית StandardProcessor מיישמת מעבד {מודל מחרוזת פרטי; // סטנדרטים / סטרים סטנדרטיים}
מחלקה ציבורית StandardMemory מיישמת זיכרון {מותג מחרוזת פרטי; גודל מחרוזת פרטי; // קונסטרוקטורים סטנדרטיים, getters, toString} 
מחלקה ציבורית StandardSoundCard מיישמת את SoundCard {מותג מחרוזת פרטי; // קונסטרוקטורים סטנדרטיים, getters, toString} 

קל להבין את המניעים שמאחורי דחיפת קומפוזיציה על פני ירושה. בכל תרחיש בו ניתן ליצור קשר "יש- a" נכון מבחינה סמנטית בין כיתה נתונה לאחרים, ההרכב הוא הבחירה הנכונה לעשות.

בדוגמה שלעיל, מַחשֵׁב עומד בתנאי "יש- a" עם הכיתות המדגמנות את חלקיו.

ראוי גם לציין שבמקרה זה, המכיל מַחשֵׁב לאובייקט יש בעלות על האובייקטים הכלולים אם ורק אם לא ניתן לעשות שימוש חוזר בחפצים בתוך אחר מַחשֵׁב לְהִתְנַגֵד. אם הם יכולים, נשתמש בצבירה, במקום בהרכב, כאשר הבעלות אינה משתמעת.

5. קומפוזיציה ללא הפשטה

לחלופין, היינו יכולים להגדיר את יחסי הקומפוזיציה על ידי קידוד קשה של התלות של ה- מַחשֵׁב בכיתה, במקום להכריז עליהם בקונסטרוקטור:

מחשב מחלקה ציבורית {מעבד StandardProcessor פרטי = StandardProcessor חדש ("Intel I3"); זיכרון StandardMemory פרטי = StandardMemory חדש ("קינגסטון", "1TB"); // שדות / שיטות נוספות}

כמובן, זה יהיה עיצוב נוקשה ומצומצם היטב, כפי שאנו מכינים מַחשֵׁב תלוי מאוד ביישומים ספציפיים של מעבד ו זיכרון.

לא ננצל את רמת ההפשטה שמספקת ממשקים והזרקת תלות.

עם התכנון הראשוני המבוסס על ממשקים, אנו מקבלים עיצוב מצויד באופן רופף, שגם קל יותר לבדוק אותו.

6. מסקנה

במאמר זה למדנו את יסודות הירושה וההרכב בג'אווה, ובדקנו לעומק את ההבדלים בין שני סוגי היחסים ("זה- a" לעומת "יש- a").

כמו תמיד, כל דגימות הקוד המוצגות במדריך זה זמינות ב- GitHub.


$config[zx-auto] not found$config[zx-overlay] not found