עקרון ההחלפה של ליסקוב
החלפה הוא עקרון תכנות מונחה עצמים לפיו, בתוכנת מחשב, אם S הוא תת-טיפוס של T, אז אובייקטים מטיפוס T עשויים להיות מוחלפים עם אובייקטים מטיפוס S מבלי לשנות את ההתנהגות של T (נכונות, ביצוע משימות, ועוד). עקרון ההחלפה של ליסקוב הוא הגדרה מסוימת של קשר בין טיפוסים לתת-טיפוסים שנקרא strong) behavioral subtyping) שהוצג לראשונה ב-1987 על ידי ברברה ליסקוב, וב-1994 יחד עם ג'נט ווינג היא ניסחה את העיקרון בתמציתיות על דף נייר כדלקמן:
תהי (f(x תכונה שניתן להוכיח את נכונותה עבור אובייקטים x מטיפוס T, ויהי S תת-טיפוס של T, אזי (f(y צריכה להיות נכונה עבור אובייקטים y מסוג S.
עקרון
הרעיון של ליסקוב לגבי טיפוסיות מגדיר את הרעיון של החלפה עבור אובייקטים; כלומר, אם S הוא תת-טיפוס של T, אזי אובייקטים מטיפוס T בתוכנית עשויים להיות מוחלפים עם אובייקטים מטיפוס S מבלי לשנות את התנהגות התוכנית.
בצורה מעט פחות פורמלית, כך ניסח רוברט מרטין את העיקרון: תוכנית שעושה שימוש ביכולת של אובייקט מטיפוס מחלקת-אב חייבת להיות מסוגלת להשתמש באובייקט מטיפוס תת-מחלקה של מחלקת האב, מבלי לדעת שהיא עושה זאת.
דוגמה
הדוגמה הקלאסית בה משתמשים בשביל להדגים את העיקרון, היא מחלקה של מלבן, ותת-מחלקה של ריבוע שיורשת ממנה.
class Rectangle
{
virtual void setWidth(int width) { this->width = width; }
virtual void setHeight(int height) { this->height = height; }
public:
int getWidth() { return this->width; }
int getHeight() { return this->height; }
protected:
int width, height;
};
עבור מחלקת המלבן, המתודות setWidth ו-setHeight טריוויאליות. לעומת זאת, עבור מחלקת ריבוע, לא נוכל לאפשר רוחב ואורך שונים.
class Square : public Rectangle
{
void setWidth(int width) override { this->width = this->height = width; }
void setHeight(int height) override { this->setWidth(height); }
};
כעת, משתמש נעזר במחלקות אלו, ואין הוא מודע כלל לכך שאם הוא יעביר אובייקט מטיפוס ריבוע לפונקציה שלו, מאחורי הקלעים מחלקת ריבוע תשנה לו את האורך או הרוחב וההנחה שהוא מניח לגבי שטח הצורה כלל איננה נכונה!
int area(Rectangle& rectangle)
{
return (rectangle.getWidth() * rectangle.getHeight());
}
void f(Rectangle& rectangle)
{
rectangle.setWidth(2);
rectangle.setWidth(4);
//area(rectangle) == 8
}
הבעייתיות במקרה זה נגרמת מכיוון שלא נשמר עקרון ההחלפה של ליסקוב, במחלקה היורשת שינינו את התנהגות הפונקציה כך שכבר לא ניתן להשתמש בה בפונקציות המוגדרות עבור מחלקת האב. לפי עקרון זה, מחלקת ריבוע לא צריכה לרשת ממחלקת מלבן כיוון שההתנהגות של ריבוע שונה מהתנהגות של מלבן ולא ניתן לקיים אותם כמחלקה ותת-מחלקה.
ראו גם
22459030עקרון ההחלפה של ליסקוב