נט2יו - איכות ברשת משנת 2004
  עקרון ההחלפה של ליסקוב
18/5/2017 3:33

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


היא נחשבת לפורצת דרך עבור נשים – היא מהראשונות לקבל תואר דוקטור למדעי המחשב (1968) ואחת משלוש בלבד שזכו בפרס טיורינג (2008) ב – 49 השנים בהן הוא מחולק.

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

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


לפני שנצא לדרך, הקדמה למצטרפים החדשים:

הפוסט הזה הוא פוסט שלישי בסדרת S.O.L.I.D / posts שבה אני מפרט על חמשת העקרונות הראשונים לעיצוב תוכנה מונחית עצמים.

אני ממליץ לקרוא על שני העקרונות הראשונים לפני שממשיכים לעקרון השלישי, שכן הם הכרחיים כדי ליישם אותו כמו שצריך:



שנתחיל?


הגדרת הקשר בין תת-סוג לסוג האב


ברברה ליסקוב הציגה לראשונה את עקרון ההחלפה (באנגלית: Liskov Substitution Principle) בשנת 1987.

המאמר שבו העקרון הוצג נכתב בשפה מאד טכנית ויבשה, עד כדי כך שאפילו בקהיליית מדעני המחשב התקשו לאמץ את העקרון:


אם עבור כל אובייקט o1 מסוג S ישנו אובייקט o2 מסוג T כך שעבור כל תוכנה P המוגדרת במונחים של T, ההתנהגות של P לא תשתנה כאשר o2 יוחלף ב – o1,

אז S הינו תת-סוג של T


אני הולך להמר שלפחות 80% מהקוראים לא הצליחו להבין מה מתרחש בנוסחה למעלה וזה בסדר: לפני שלמדתי את העקרון, גם אני לא הצלחתי להבין מה הגברת ליסקוב רוצה מחיי.

כשאני חושב על זה, זה מעט אירוני: עקרון ההחלפה של ליסקוב, לטעמי, הוא העקרון הפשוט ביותר מבין חמשת העקרונות.

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

לפני שאני מציג בפניכם את ההגדרה, חשוב לי ליישר קו ולוודא שאתם מכירים את המונחים "מחלקת בסיס" ו – "מחלקה נגזרת".


בקטע הקוד הבא:

class A {
// ...
}

class B extends A {
// ...
}

מתוארות שתי מחלקות – A ו – B.



  • A – מחלקת בסיס: מחלקה ממנה יורשת מחלקה אחרת (B)

  • B – מחלקה נגזרת: מחלקה שיורשת ממחלקה אחרת (A)


עבור קטע הקוד למעלה, ניתן לומר כי המחלקה B  היא מחלקה נגזרת ממחלקת הבסיס A.


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


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


עקרון ההחלפה של ליסקוב: על ריבועים ומלבנים


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

מוצגת לפניכם מחלקה שמתארת מלבן:

class Rectangle {
protected $width;
protected $height;

public function setWidth($w) {
$this->width = $w;
}

public function setHeight($h) {
$this->height = $h;
}

public function getWidth() {
return $this->width;
}

public function getHeight() {
return this->height;
}
}

בבואנו להגדיר ריבוע, נשתמש במחלקה Rectangle כמחלקת-בסיס.


וכאן אנחנו נתקלים בבעיה. ויקיפדיה מגדירה ריבוע:


ריבוע הוא מרובע בעל ארבע צלעות שוות


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

כדי להתגבר על הבעיה, נדרוס את הפונקציות setWidth ו – setHeight כשניישם את המחלקה Square:

class Square extends Rectangle {

public function setWidth($w) {
$this->width = $this->height = $w;
}

public function setHeight($h) {
this->setWidth($h);
}
}

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

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


אבל מה יקרה כשנכתוב פונקציה שמטפלת בטיפוס נתונים Rectangle? (רמז: בניינים יתחילו להתמוטט..)


ניקח לדוגמה את הפונקציה הבאה, שמקבלת כפרמטר עצם מסוג Rectangle, קובעת רוחב (5) וגובה (4) ואז בודקת באמצעות הפקודה assert האם רוחב כפול גובה שווה 20:

public function g(Rectangle $r) {
$r->setWidth(5);
$r->setHeight(4);
assert ($r->getWidth() * $r->getHeight() == 20);
}

זכרו: על-פי עקרון ההחלפה של ליסקוב, על הפונקציה לדעת להתמודד עם כל עצם מסוג Rectangle!


מי שכתב את הפונקציה g הניח שבעת שינוי רוחב של מלבן, הגובה נשאר כפי שהוא ולהפך.

על-פניו, הנחה הגיונית וסבירה – אבל ברגע שנעביר לפונקציה Square ולא Rectangle – הפונקציה תחזיר ערך שגוי.


המחלקה Square (המחלקה הנגזרת) משנה את ההתנהגות הטבעית של המחלקה Rectangle (מחלקת הבסיס).

המשפט האחרון, גבירותיי ורבותיי, הוא תמצית עקרון ההחלפה של ליסקוב:


מחלקה מרחיבה לעולם לא תשנה את ההתנהגות הטבעית של מחלקת הבסיס


איך נפתור את הבעיה? לצערי, אני נאלץ להודיע לכם שאין פתרון קסם!


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


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

public function g(Rectangle $r) {
if($r instanceof Square) {
$r->setWidth(5);
assert ($r->getWidth() * $r->getHeight() == 25);
}
else {
$r->setWidth(5);
$r->setHeight(4);
assert ($r->getWidth() * $r->getHeight() == 20);
}
}

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


במאמרו, רוברט מרטין כתב (בתרגום חופשי): תקינות איננה פנימית. מה הכוונה?

אם נבחן בנפרד כל אחת מהמחלקות, Rectangle ו – Square, נגלה שבשורה התחתונה, שתיהן מתפקדות כצפוי:



  • Rectangle מאפשרת לנו לקבוע רוחב וגובה למלבן

  • Square מאפשרת לנו לקבוע אורך צלע


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

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


כלל האצבע


כשלמדתי על עקרון ההחלפה של ליסקוב, נתקלתי בכלל האצבע הבא:


מחלקות ממומשות צריכות להיות סופיות


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



  1. אנחנו כבולים ליישום של מחלקת הבסיס

  2. אנחנו עשויים לדרוס פונקציות קיימות

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


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

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


מקורות



הפוסט עקרון ההחלפה של ליסקוב הופיע ראשון בMasterScripter







האחריות על התגובות למאמרים השונים חלה על שולחיהן. הנהלת האתר אינה אחראית על תוכנן.
שליחת תגובה
חוקי שליחת תגובות*
תגובות חברי האתר מאושרות אוטומטית
כותרת*
_CM_USER*
_CM_EMAIL*
_CM_URL*
הודעה*
קוד אבטחה*

 הערה: התכנים המוצגים בעמוד זה ...

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

הצהרת נגישות

אתר זה מונגש לאנשים עם מוגבלויות על פי Web Content Accessibility Guidelines 2 ברמה AA.
האתר נמצא תמידית בתהליכי הנגשה: אנו עושים כל שביכולתנו שהאתר יהיה נגיש לאנשים עם מוגבלות.
אם בכל זאת נתקלתם בבעיית נגישות אנא שלחו לנו הערתכם במייל (אל תשכחו בבקשה לציין את כתובת האתר).

אודות ההנגשה באתר:

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