תכנות מונחה עצמים | Liskov Subtitution Principle

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

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

היום אני הולך להציג את עקרונות העיצוב SOLID. אלו הם 5 העקרונות הראשונים והחשובים ביותר בעיני של עיצוב תוכנה וקוד באמצעות תכנות מונחה עצמים.

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

תנאי קדם למדריך: היכרות עם תכנות מונחה עצמים.

מה זה SOLID?



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

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

עקרון ההחלפה של ליסקוב - Liskov Subtitution Principle





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

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

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

דוגמה ב-C# על LSP

ממש כמו בתמונה, נלך על דוגמה של שתי מחלקות המממשות Interface בשם IDuck




בפעולה Swim, נשנה את IsSwimming ל true.

לאחר מכן ניצור שתי מחלקות, אחת של ברווז אמיתי, והשניה של ברווז חשמלי.

* הברווז החשמלי Electronic Duck יוכל לשחות רק אם הוא דולק

המחלקות הממשות יראו כך:



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

אז איפה הבעיה?

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

הפעולה תראה כך:

הלקוח מכיר את הממשק IDuck ואת הפעולות שהוא יודע לעשות, אבל הבעיה היא שהלקוח לא יודע שצריך להדליק את הברווז האלקטרוני, כי אם לא ידליקו אותו, הוא לא ישחה!

במילים אחרות, חיללנו את LSP! פגענו בהתנהגות הממשק - כעת מחלקת הבן (ElectronicDuck) לא תוכל לבצע את הפעולות של IDuck באופן טבעי.

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


אבל גם כאן יש בעיה לא קטנה!
כאן אנחנו מחללים את החוק הקדוש - Open/Closed Principle - שאומר שאסור לנו לפתוח את הפעולה שלנו לשינויים אלא רק להרחבות.

במילים אחרות, מה יקרה אם מחר נקבל סוג אחר של ברווז שצריך לבצע פעולה אחרת לפני שמתחיל לשחות? נוסיף לפעולה MakeDuckSwim עוד if?

אז מה לעזאזל הפתרון?

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

במקרה זה הפתרון יהיה להדליק את הברווז כבר בפעולה Swim.

הפעולה Swim במחלקה ElectroinicDuck:


סיכום

היום למדנו על עקרון ההחלפה של ליסקוב - LSP, עיקרון L ב "Solid Prinicples" - שאומר שמופע של אובייקט ממחלקת הבן תמיד יוכל להחליף מופע של אובייקט ממחלקת האב.

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

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

נתראה בפוסט הבא :)

תגובות

  1. אם הוספת את השורה:
    this.TurnOn();
    אז למה לא מחקת את התנאי שבודק שהברווז האלקטרוני דולק?

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

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

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

    אשמח להסבר. תודה

    השבמחק

הוסף רשומת תגובה

פוסטים פופולריים מהבלוג הזה

תכנות מונחה עצמים | Dependency Inversion Principle

מהם קבצי DLL ואיך להשתמש בהם?

מה ההבדל בין אוטומציה לפיתוח רגיל