Table of Contents
בתכנות ממוקדת התנגדות, מערכות בנייה שהן חזקות והתאמה הן האתגר הנצחי.בין העקרונות הבסיסיים המנחים מפתחים לעבר מטרה זו הוא ה- Liskov Substitution Principle (LSP) המסופק על ידי ברברה ליסקוב ב-1987, LSP הוא עמוד השלישי של חמשת עקרונות SOLID לתכנון תוכנה, והוא מתייחס לשאלה קריטית: כאשר אתה יוצר מחלקה אשר יכול להחליף את הדוגמאות של כיתת האם היחידה, אם אתה יכול להחליף את ה-ה של כל אחד מהם, אם אתה יכול להחליף את ה-ה של כל אחד מהם הוא רק אם אתה יכול להחליף את ה-ה של תוכנית המשנה של ה-ה של ה-ה של ה-ה של ה-המתורה באופן בטוח, אם אתה יכול להחליף את ה-זמנית של תוכנית ה-זמנית של תוכנית ה-זמנית של ה-זמנית של תוכנית ה-זמנית של ה-זמנית של ה-המחלקה הזאת, אם אתה יכול להחליף את ה-הסוגיתורה, אם אתה יכול להחליף את ה-הדוקטורטבחירות של ה-זמנית של ה-זמנית של ה-זמנית של ה-זמנית של ה-זמנית של ה-זמנית של תוכנית ה-זמנית של תוכנית ה-זמנית של ה-זמנית של ה-זמנית של ה-זמנית של
מהו עיקרון ה- Liskov?
ה- Liskov Substitution Principle קובע כי אובייקטים של סופר-class צריך להיות להחליף אובייקטים של תת-המעמד שלה מבלי להשפיע על נכונות התוכנית. במילים אחרות, אם פונקציה או שיטה נועדו לעבוד עם סוג בסיס, זה צריך לעבוד עם כל סוג נגזר ללא צורך שינוי או ייצור תופעות לוואי בלתי צפויות.
LSP הוא ביסודו על תת-התנהגותיות subtyping.It's לא מספיק כי תת-קבוצה יש אותה שיטה חתימה כמו ההורה שלה (קונפורציה סינקטית); ה- subclass חייב גם לכבד את הכוונות והמגבלות של המעמד ההורה.אם תת-מעמד משנה את ההתנהגות הבסיסית של שיטת הורה - למשל, על ידי זריקת חריגים ההורה לעולם לא זורק, להחזיר ערך המפר את החוזה של ההורה, או דורש תנאים נוקשים יותר, אז לא מתאים, לא בטוח.
הגדרה ורקע
ההגדרה הרשמית המקורית של ברברה ליסקוב היא כדלקמן:
(ב) אם לכל חפץ יש ⁇ :0) 1 (ב) 1 (ב) ,ב) , (ב) , (ב) ,23 ,23) , ).
הגדרה זו מדגישה כי תת-סוג (S) חייב להיות חלק בלתי נפרד עבור סופר-טיפוס שלה (T) בכל תוכנית (P) שנכתבה במונחים של T.ההתנהגות הבלתי ניתנת להחלפה של התוכנית יש לשמר.מושג זה קשור קשר הדוק לתכנון על ידי מתודולוגיה (DbC) אשר נכתבה על ידי Bertrand Meyer, שבו לכל שיטה יש תנאים מפורשים (מה צריך להיות אמיתי) ובלבד תנאים מוקדמים (לא יכול להיות חלש) רק לאחר תנאים מוקדמים (התנאים לפני ה-Lacten) ו-LUTFactuts) יכול להיות חלש (התנאים לפני כן, רק לאחר תנאי לפני כן, רק לאחר תנאי מוקדם יותר).
לקריאה נוספת, ראה את המקור:0 [ליסקוקוב ונייר כנף] 1 אשר הסדיר את הרעיון.
למה חשוב ל-LSP?
אישור ל-LSP מביא מספר יתרונות קריטיים במערכות מוכוונות להתנגדות:
- קוד המקור:0 (אחריות ותיקון: צופן 1FIRLT) המשתמש בסוג בסיס יכול להאמין שכל תת-מעמד תת-מעמד תתנהג בהתאם לחוזה הבסיס.
- (FLT:0) פופלמורפיזם: 1FLT:1 Polymorphism הוא היכולת לטפל אובייקטים של שיעורים שונים באמצעות ממשק משותף.ללא LSP, פולימורפיזם הופך מסוכן כי החלפת תת-class עשויה לייצר תוצאות לא נכונות.LSP מבטיח כי קוד פולימורפיל פועל כפי שנועד.
- (FLT:0) אחריות וחידושיות: ⁇ 1 כאשר הפרות LSP נמנעות, הוספת תת-classes חדשים לא דורש שינוי קוד קיים תלוי בסוג הבסיס.זה תואם את הקוד הפתוח / הסגור Principle (OCP) - ישויות תוכנה צריכות להיות פתוחות להרחבה אך סגורות לשינויים.
- (FLT:0) מבחנים יחידה 1:1 שנכתב נגד מעמד בסיס ניתן להשתמש בו כדי לאמת תת-מעמדות.אם תת-מעמד מפרה את LSP, בדיקות אלה יכשלו, לחשוף את חוסר עקביות מוקדם.
LSP הוא לא רק מושג אקדמי; יש לו השלכות מעשיות ישירות.לדוגמה, במערכת עיבוד תשלום, אם יש לך בסיס FLT:0 שיעור עם שיטת 1FLT, אתה מצפה שכל תת-classes (למשל, FLT:2, FLT 3: 3) כדי לעבד תשלומים ללא שגיאות או תופעות לוואי כי מעמד הבסיס אינו צופה הפרות כאן יכול להוביל הכנסה או ירידה של נתונים מושחתים.
הפרות נפוצות של ה- Liskov Substitution Principle
זיהוי הפרות LSP הוא הצעד הראשון לתיקון אותם.כאן כמה דפוסים אופייניים המפרקים את העיקרון:
חיזוק תנאי קדם
אם שיטת מעמד בסיס צופה פרמטר integer, הוספת מצב בתת-המעמד כי האינטגרטור חייב להיות חיובי (בעוד שמעמד הבסיס מקבל כל אינסטלגר) מחזק את תנאי מוקדם.
// Base class
class UserService {
public void assignRole(int userId) { ... }
}
// Subclass violation
class AdminService extends UserService {
@Override
public void assignRole(int userId) {
if (userId <= 0) throw new IllegalArgumentException("Invalid ID");
...
}
}
המונחים:
אם מעמד הבסיס מבטיח ערך החזרה מסוים או אפקט צד, תת-קבוצה המפחיתה את ה-LSP. לדוגמה, שיטת מעמד בסיס יכולה תמיד להחזיר מחרוזת לא-נול; תת-קבוצה מחזירה את ה-FLT:5 במקרים מסוימים מחלישה את תנאי הדואר.
לזרוק את החצאית החדשה
לא צריך לזרוק חריגים כי מעמד הבסיס אינו זורק (אלא אם החריגים האלה הם תת-מעמד של חריגים שכבר הורשו) אם לקוחות של מעמד הבסיס תופסים רק 6, וקבוצה תת-קבוצה זורקת חלק (FLT 7), ההחלפה תגרום לתאונות בלתי צפויות.
הסרת שיטות או overriding כי צריך להיות inherited
אם תת-קבוצה מתעלמת משיטת לא לעשות דבר (גוף ריק) או לזרוק את ה-FLT:8, זו עבירה ברורה.
דוגמה קלאסית: Rectangle, Square, and the Area Trap
הדוגמא המצוטטת ביותר של הפרת LSP כוללת צורות גאומטריות.מספר ספרי לימוד מתחילים עם שיעור 9 (FLT:10 ו-FLT:11 שיטות, ולאחר מכן ליצור תת-קרקעית FLT:12 אשר מעלים את אלה כדי לאכוף את הגובה.
// Base class
class Rectangle {
protected int width, height;
public void setWidth(int w) { width = w; }
public void setHeight(int h) { height = h; }
public int getArea() { return width * height; }
}
// Subclass violation
class Square extends Rectangle {
@Override
public void setWidth(int w) {
width = height = w;
}
@Override
public void setHeight(int h) {
width = height = h;
}
}
עכשיו יש לקחת חלק ב-[[1924]]:
void resize(Rectangle r) {
r.setWidth(5);
r.setHeight(10);
assert r.getArea() == 50; // Expect 50
}
(ב) כאשר הוא עובר ל-5 שנים, אך ה-FLT (ה-FLT) של ה-[[1924]], [[1924]], [[1924]], [[1924]], [[1924]]]], [[1924]]]]]], [[1924]]]]]]]], [[1924]]]]]], [[1924]]]]]]]], [[1924]]]]]]]], [[1924]]]]]]]], [[1924]]]]]], [[1924]], [[1924]]]]]]]]]]]], [[1924]]]]]]]], [[1924]]]], [[1924]]]]]]]]]]]], [[1924]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]], [[1924]]]]]]]]]]]]]], [[1924]], [[1924]], [[1924]], [[1924]]]], [[1924]]]]]], [[1966]]]], [[1924]]]]]]]]]], [[1924]], [[1924]]]], [[1924]], [[1924]], [[1924]]]]]]]], [[1924]], [[1924
[ה] [ה]] [ה]] [ה]] [ה]] [ה]] [ה]]][ה]]]][ה]]]][ה]]]][ה]]]], [ה'[ה]'[ה']'[ה']'[ה']']'[ה']'[ה']'[ה'[ה']']']'[ה'[ה']']'[ה']'[ה'[ה'[ה'[ה'[ה'[ה']']'[ה'[ה'[ה'[ה'[ה']']']']'[ה']']'[ה'[ה'[ה']']']']']']'[ה'[ה']']'[ה'[ה'[ה']']']'[ה'[ה']'[ה'[ה'[ה'[ה']'[ה']']']'[ה'[ה'[ה'[ה'[
דוגמה אמיתית לעולם: אינטגרציה של שער תשלום
דמיינו מערכת מסחר אלקטרוני עם מחלקה בסיסית:30
abstract class PaymentGateway {
public abstract void charge(double amount);
public void refund(double amount) { /* default implementation */ }
}
(ב) , (ב) , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
(ב) [ה]: [ה] [ה]] [ה]]] [ה]]], [ה], [ה]]], [ה]], [ה]]]] [ה]]], [ה], [ה], [ה]], [ה] לא ניתן להשיג] את ההיררכיה, כך שלא כל ה-GatephsreliF:42 ו-F:42 ו-Eramsto-Eration] יאשר לא יחזיר את ה-Eram [הההההההההההההההההההההההההההה'] ל-[[ה'] את ה'] ל'] את ה'] ל'] ל'] את ה', ולא יברך' [ה' [ב'], ולא יברך'[ה' [ה', ולא תשובים'], ולא יברך את ה'.
כיצד לעקוב אחר ה- Liskov Substitution Principle in Practice
יישום LSP דורש משמעת בעיצוב ובדיקה.כאן הם קווים מנחים מעשיים:
- (FLT:0) עיצוב על ידי חוזים:FLT:1 ברור לציין תנאים מוקדמים, תנאי דואר, וחלודות עבור שיטות מעמד בסיס.עד מה כל שיטה מצפה ומבטיחה.אז להבטיח כל complies תת-כית. כלים כמו חוזים ב .NET או JML עבור Java יכולים לעזור לאכוף את החוקים האלה.
- (FLT:0) Interfaces Over אבסטרקטי: Interfaces מגדיר חוזה ללא פרטי יישום.הם מתאימים באופן טבעי ל-LSP כי כל מחלקה צריכה למלא את כל הממשק עם שיעורים מופשטים, קל יותר להציג בטעות תלות.
- (ב) כאשר תת-קבוצה תצטרך להתגבר על התנהגות עד כדי פירוק החוזה, לעתים קרובות עדיף להשתמש בהרכב.
- (FLT:0) לרכיבה: ⁇ 1) כתוב בדיקות פרמטריות שעושות את אותם תרחישים נגד שני הסוגים הבסיסים והגזרה.אם מבחן עובר עם סוג הבסיס אך נכשל עם סוג נגזר, יש לך הפרה LSP.
- (FLT:0)Check for Covariant Return Types: ההרחבה 1 (כמה שפות מאפשרות ל- covariant Return Types (למשל, שיטת תת-כית יכולה להחזיר סוג ספציפי יותר מהבסיס) זה בסדר כל עוד זה לא משנה את תנאי הדואר.לוודא שהאובייקט השבוי עדיין משביע את כל הציפיות של הערך של החזר הבסיס.
- (ב) [ה]:0] הימנעות משיטות קונקרטציה חסרות הבחנה: אם אתה מרגיש צורך לעקוף שיטה קונקרטית בת-קבוצה, השאלה אם הירושה היא הכלי הנכון.אולי מעמד הבסיס היה קונקרטי מדי.
עקרונות LSP ו-SOLID
LSP קשור עמוק לעקרונות SOLID האחרים, במיוחד את הקוד הפתוח / הסגור (OCP) ואת הכדאיות Inversion Principle (DIP):
- (FLT:0)LSP ו- OCP: FLT:1 OCP קובע כי שיעורים צריכים להיות פתוחים להרחבה אך סגורים לשינוי.LSP מבטיח כי הרחבות (סעיפים) אינן לשבור את הקוד הקיים המשתמש בסוג הבסיס.ללא LSP, הוספת תת-קבוצה חדשה תדרוש לשנות את קוד הלקוח כדי לטפל בהתנהגות החדשה, בהפרת OCP.
- (FLT:0)LSP ו- DIP: DIP מייעץ בהתאם לפשטות, לא קונסרציות.LSP הוא חיוני כאן כי הפשטות (interface or Base Class) חייבת להיות יציבה ואמינה.אם תת-קבוצות מפרות את LSP, הפשטות אינה עוד תלות אמינה, והמערכת הופכת לשברירית.
- (FLT:0)LSP ו- Interface Segregation (ISP): 1 ISP מעודד ממשקים קטנים וממוקדים.זה תומך באופן טבעי ב-LSP משום שממשק קטן מגדיר חוזה חזק שקל יותר לכבד. מחלקה שמיישם ממשק גדול יותר עלולה להיאבק כדי למלא את כל החלקים, מה שמוביל להפרות כמו זריקת FLT:49).
לסקירה מקיפה של עקרונות SOLID, לבדוק את המאמר של SOLID:0 וikipedia's SOLID מאמר 1 .
מסקנה
ה- Liskov Substitution Principle הוא הרבה יותר מנחמד תיאורטי; זהו כלי מעשי לבניית מערכות מוכוונות אובייקט בטוח להרחיב וקלות לשמור. על כך שמשתתפים מתנהגים באופן שהוא חלק בלתי נפרד לשיעורי ההורה שלהם, מפתחים יכולים לסמוך על פולימורפיזם ללא חשש של באגים נסתרים.
(ל) לעיתים קרובות מופיעים הפרות של חוסר עקביות עדינות בהתנהגות: שיטה שזרקה יוצא דופן בלתי צפוי, שיטה שאותה אין לעשות דבר, או שיטה שמשנה את המדינה בדרך שבה מעמד הבסיס מעולם לא התכוון.על ידי היותו מודע לדגלים האדומים הללו, ועל ידי יישום ההנחיות שנדונו לעיל, תוכל ליצור היררכיות שעומדות במבחן הזמן.
הפניות חיצוניות המשמשות במאמר זה:
- (ב) "העדר התנהגות של צוללות" - ליסקוב ואגף (1994)
- (ב) ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
- (ב) ⁇ ⁇ ⁇ ⁇ ⁇
- (ב) ויקרא יא"ד: "התב" (ב"ג)