1 / 71

עיצוב בעזרת חוזים Design by Contract

עיצוב בעזרת חוזים Design by Contract. עמירם יהודאי אוניברסיטת תל אביב. סמינר מורים מובילים קיץ 2009. על סדר היום. מוטיבציה חוזים ירושה וחוזים תמיכת כלי תוכנה בחוזים כלים מתקדמים וכיווני מחקר חלק מהשקפים נלקחו משקפי הקורס תוכנה 1 באוניברסיטת תל אביב. מוטיבציה. מערכת תוכנה.

sahara
Download Presentation

עיצוב בעזרת חוזים Design by Contract

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. עיצוב בעזרת חוזיםDesign by Contract עמירם יהודאי אוניברסיטת תל אביב סמינר מורים מובילים קיץ 2009

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

  3. מוטיבציה

  4. מערכת תוכנה • מערכת תוכנה נבנית מאוסף של מודולים. • חלוקה למודולים צריכה לקיים: • לכל מודול חוזק פנימי מירבי • צימוד חלש ככל האפשר בין מודולים • לצורך הפרדת עניינים (separation of concern), כותבי מודול צריכים לדעת רק מה שנחוץ על מודולים אחרים בהם הם משתמשים. • כאשר המערכת הנבנית משתמשת בספרית תוכנה, שמסופקת ללא קבצי מקור (משיקולי זכויות יוצרים), מספקים למפתחים תיעוד מתאים. עיצוב בעזרת חוזים

  5. API מקובל לספק עם הספריות מסמך תיעוד, המפרט את שמות וחתימות של המחלקות, הפעולות והתכונות יחד עם תיאור מילולי של אופן השימוש בהם חתימה של פעולה: השם, טיפוסי הפרמטרים, וטיפוס הערך המוחזר לדוגמא, בג'אוה מקובל להשתמש ב javadoc, כלי תוכנה שמחולל תיעוד אוטומטי בפורמט html על בסיס הערות התיעוד שהופיעו בגוף קובצי המקור תיעוד זה מכונה API (Application Programming Interface) מעכשו נתייחס לתכנות מונחה עצמים: מחלקות הן המודולים חלק מהעקרונות רלבנטיים גם לתכנות פרוצדורלי ופונקציונלי עיצוב בעזרת חוזים 5

  6. הגדרת השאלה • מבחינתנו אין חשיבות אם אנחנו משתמשים בספרית תוכנה שקוד המקור שלה חסוי, או ברכיבים (מחלקות) שנכתבו בארגון שלנו על ידי צוות אחר, מתכנתת אחרת, או אפילו על ידינו. • ספק: מחלקה שנכתבה (או תיכתב) • לקוח: מחלקה שנכתבת ומשתמשת בשרותים מהספק • שאלה: איזה מידע על מחלקת הספק יש להעמיד לרשות מפתחת מחלקת הלקוח? • במילים אחרות: מה מכיל ה API של הספק? • ובמיוחד: האם חתימה + תאור מילולי מספיקים? עיצוב בעזרת חוזים

  7. פערי הבנה • חתימה אינה מספיקה, מכיוון שהספק והלקוח אינם רק שני רכיבי תוכנה נפרדים אלא גם לפעמים נכתבים ע"י מתכנתים שונים עשויים להיות פערי הבנה לגבי תפקוד שרות מסוים • הפערים נובעים ממגבלות השפה הטבעית, פערי תרבות, הבדלי אינטואיציות, ידע מוקדם ומקושי יסודי של תיאור מלא ושיטתי של עולם הבעיה • לדוגמא: נתבונן בפונקציה divide המקבלת שני מספרים ומחזיר את המנה שלהם: publicstaticint divide(int numerator, int denominator) {...} • לרוב הקוראים יש מושג כללי נכון לגבי הפונקציה ופעולתה • למשל, ברור מה תחזיר הפונקציה אם נקרא לה עם הארגומנטים 6 ו- 2 עיצוב בעזרת חוזים

  8. מה קורה במקרים אחרים? • אך מה יוחזר עבור הארגומנטים 7 ו- 2 ? • האם הפונקציה מעגלת למעלה? מעגלת למטה? • ועבור ערכים שליליים? • אולי היא מעגלת לפי השלם הקרוב? • ואולי השימוש בפונקציה אסור בעבור מספרים שאינם מתחלקים ללא שארית? • מה יקרה אם המכנה הוא אפס? • האם נקבל ערך מיוחד השקול לאינסוף? • האם קיים הבדל בין אינסוף ומינוס אינסוף? • ואולי השימוש בפונקציה אסור כאשר המכנה הוא אפס? • מה קורה בעקבות שימוש אסור בפונקציה? • האם התוכנית תעוף? • האם מוחזר ערך שגיאה? אם כן, איזה? • האם קיים משתנה או מנגנון שבאמצעותו ניתן לעקוב אחרי שגיאות שארעו בתוכנית? עיצוב בעזרת חוזים

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

  10. חוזים

  11. עיצוב על פי חוזה (design by contract) • הגישה הוצעה על ידי מפתח שפת התכנות אייפל, ברטראן מאייר (Bertrand Meyer) • מבוססת על עבודות של Hoare ו Floyd • אייפל כוללת תחביר מיוחד להגדרת חוזים כחלק מהשפה • אנחנו נציג את הגישה בשפת Java, שלא כוללת תחביר מיוחד לציון החוזה, ואולם אנחנו נתבסס על תחביר המקובל במספר כלי תכנות • נתייחס לפעולות שהמחלקה מייצאת, ונבחין בין • שאילתות: פעולות שמחזירות ערך ולא משנות את מצב העצם • פקודות: פעולות שמשנות את מצב העצם, ואינן מחזירות ערך עיצוב בעזרת חוזים

  12. עיצוב על פי חוזה (design by contract) נציין בהערות התיעוד שמעל כל פונקציה: תנאיקדם (precondition) – מהן ההנחות של כותב הפונקציה לגבי הדרך התקינה להשתמש בה תנאי בתר (תנאי אחר, postcondition) – מה עושה הפונקציה, בכל אחד מהשימושים התקינים שלה נשתדל לתאר את תנאי הקדם ותנאי הבתר במונחים של ביטויים בולאנים חוקיים ככל שניתן (לא תמיד ניתן) שימוש בביטויים בולאניים חוקיים (נקרא להם טענות): מדויק יותר יאפשר לנו בעתיד לאכוף את החוזה בעזרת כלי חיצוני עיצוב בעזרת חוזים 12

  13. חוזה אפשרי ל- divide /** *@predenominator!=0, *“Can’t divide by zero" * *@postMath.abs($ret*denominator)<= Math.abs(numerator) , *"alwaystruncatesthefraction" * *@post(($ret * denominator) + (numerator % denominator)) *==numerator, "regulardivide" */ publicstaticint divide(int numerator, int denominator) • $ret מציין את הערך שהפונקציה מחזירה • לפעמים החוזה ארוך יותר מגוף הפונקציה עיצוב בעזרת חוזים

  14. חוזה אפשרי אחר ל- divide /** *@pre(denominator!=0)||(numerator!=0), *"youcan'tdividezerobyzero" * *@post(denominator==0)&&((numerator>0)) *$implies$ret==Integer.MAX_VALUE *"Dividingpositivebyzeroyieldsinfinity (MAX_INT)" * *@post(denominator==0)&&((numerator< 0)) $implies *$ret==Integer.MIN_VALUE *"Dividingnegativebyzeroyields minus infinity (MIN_INT)" * *@postMath.abs($ret*denominator)<= Math.abs(numerator) , *"alwaystruncatesthefraction" * *@post(denominator!=0)$implies * (($ret*denominator)+(numerator%denominator))==numerator, *"regulardivide" */ publicstaticint divide(int numerator, int denominator) $implies הוא האופרטור גרירה לוגית עיצוב בעזרת חוזים תנאי קדם סובלניים מסבכים את מימוש הפונקציה – כפי שמתבטא בחוזה

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

  16. /** @inv (top >= 0 && top < max) */ class MyStack { private Object[] elems; private int top, max; /** @pre (sz > 0) @post (max == sz && elems != null) */ public MyStack(int sz) { max = sz; elems = new Object[sz]; } אינוריאנטה תנאי קדם תנאי בתר עיצוב בעזרת חוזים 16

  17. /** @pre !isFull() @post (top == $prev (top) + 1) && elems[top-1] == obj */ public void push(Object obj) { elems[top++] = obj; } /** @pre !isEmpty() @post (top == $prev (top) - 1) */ public void pop() { top--; } /** @pre !isEmpty() @post $ret == elems[top] */ public Object top() { return elems[top]; } הערך בכניסה עיצוב בעזרת חוזים 17

  18. public boolean isFull() { return top == max; } /** @post ($ret == (top == 0)) */ public boolean isEmpty() { return top == 0; } } הערך המוחזר עיצוב בעזרת חוזים 18

  19. /** @inv (top >= 0 && top < max) */ class MyStack <T> { private T[] elems; private int top, max; /** @pre (sz > 0) @post (max == sz && elems != null) */ public MyStack(int sz) { max = sz; elems = (T[]) new Object[sz]; } אינוריאנטה גירסא גנרית תנאי קדם תנאי בתר עיצוב בעזרת חוזים 19 19

  20. /** @pre !isFull() @post (top == $prev (top) + 1) && elems[top-1] == obj */ public void push(T obj) { elems[top++] = obj; } /** @pre !isEmpty() @post (top == $prev (top) - 1) */ public void pop() { top--; } /** @pre !isEmpty() @post $ret == elems[top] */ public T top() { return elems[top]; } גירסא גנרית הערך בכניסה עיצוב בעזרת חוזים 20

  21. public boolean isFull() { return top == max; } /** @post ($ret == (top == 0)) */ public boolean isEmpty() { return top == 0; } } גירסא גנרית הערך המוחזר עיצוב בעזרת חוזים 21

  22. פעולה לעולם לא תבדוק את תנאי הקדם שלה • פעולה לעולם לא תבדוק את תנאי הקדם שלה • גם לא "ליתר ביטחון" • אם פעולה בודקת תנאי קדם ופועל לפי תוצאת הבדיקה, אזי יש לה התנהגות מוגדרת היטב עבור אותו תנאי – כלומר הוא אינו תנאי קדם עוד • אי הבדיקה מאפשרת כתיבת מודולים "סובלניים" שיעטפו קריאות למודולים שאינם מניחים דבר על הקלט שלהם • כך נפריד את בדיקות התקינות מהלוגיקה העסקית (business logic) כלומר ממה שהפונקציה עושה באמת • גישת תיכון ע"פ חוזה סותרת גישה בשם "תכנות מתגונן" (defensive programming) שעיקריה לבדוק תמיד הכל עיצוב בעזרת חוזים

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

  24. דוגמא /** *@paramaAnarraysortedinascendingorder *@paramxanumbertobesearchedina *@returnthefirstoccurrenceofxina, or -1 if * it x does not occur in a * *@pre"aissortedinascendingorder" */ publicstaticint searchSorted(int [] a, int x) • האם עליה לבדוק את תנאי הקדם? • כמובן שלא, בדיקה זו עשויה להיות איטית יותר מאשר ביצוע החיפוש עצמו • ונניח שהיתה בודקת, מה היה עליה לעשות במקרה שהמערך אינו ממוין? • להחזיר 1- ? • למיין את המערך? • לחפש במערך הלא ממוין? • על searchSorted לא לבדוק את תנאי הקדם. אם לקוח יפר אותו היא עלולה להחזיר ערך שגוי או אפילו לא להסתיים אבל זו כבר לא אשמתה... עיצוב בעזרת חוזים

  25. חיזוק תנאי האחר • אם תנאי הקדם לא מתקיים, לפעולה מותר שלא לקיים את תנאי האחר כשהיא מסיימת; קריאה לשירות כאשר תנאי הקדם שלו לא מתקיים מהווה תקלה שמעידה על פגם בתוכנית • אבל גם אם תנאי הקדם לא מתקיים, מותר לפעולה לפעול ולקיים את תנאי האחר • לפעולה מותר גם לייצר כאשר היא מסיימת מצב הרבה יותר ספציפי מזה המתואר בתנאי האחר; תנאי האחר לא חייב לתאר בדיוק את המצב שייווצר אלא מצב כללי יותר (תנאי חלש יותר) • למשל, פעולה המתחייבת לביצוע חישוב בדיוק של כלשהו יכולה בפועל להחזיר חישוב בדיוק של 2/  עיצוב בעזרת חוזים

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

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

  28. משתמר הייצוג • משתמר ייצוג (representation invariant, Implementation invariant) הוא בעצם משתמר המכיל מידע פרטי (private) • לדוגמא, המשתמר של מחסנית הוא כולו משתמר ייצוג /** @inv (top >= 0 && top < max) @inv (elems != null) @inv top() == elems[top] */ class MyStack <T> { private T[] elems; private int top, max; ..... } • הבחנה במשתמר ייצוג – ע"י כלי, או תיוג שונה עיצוב בעזרת חוזים

  29. תנאי בתר ייצוגי • גם בתנאי בתר עלולים להיות ביטויים פרטיים שנרצה להסתיר מהלקוח, גם בדוגמא: /** @pre !isEmpty() @post (top == $prev (top) - 1) */ public void pop() { top--; } • אבל לא בתנאי קדם של מתודות ציבוריות • מדוע? עיצוב בעזרת חוזים

  30. דע מה אתה מבקש • מי מונע מאיתנו לעשות שטויות? • אף אחד • קיימים כלי תוכנה אשר מחוללים קוד אוטומטי, שיכול לאכוף את קיום החוזה בזמן ריצה ולדווח על כך • פרטים בהמשך • השימוש בהם עדיין לא נפוץ מספיק • אולם, לציון החוזה (אפילו כהערה!) חשיבות מתודולוגית נכבדה בתהליך תכנון ופיתוח מערכות תוכנה גדולות עיצוב בעזרת חוזים

  31. האם זה מזכיר לכם משהו? • פקודת assert קיימת במספר שפות assert <boolean-expression> • המשתמש מפעיל את הפקודות האלה ע"י אופציה של הקומפילר • הביטוי הבוליאני <boolean-expression> מחושב • אם ערכו trueלא קורה שום דבר • אם ערכו falseמורם חריג (exception), עם הודעה מתאימה. • ניתן לבטא את החוזים בעזרת פקודות assert • בתחילה/סוף גוף פעולה עבור תנאי קדם/אחר • כדי לבטא $prev נצטרך לשמור בעצמנו ערכים • assert עבור משתמר יצטרך לחזור בכל פעולה • עדיפה הגישה שמבחינה בין קוד לחוזה • ושליטה יותר טובה איזה חוזים ייבדקו בזמן ריצה (פרוט בהמשך) עיצוב בעזרת חוזים

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

  33. קבלנות משנה -על ירושה וחוזים

  34. תכנות ע"י חוזה וירושה טענות קדם, בתר ואינורינטות של מחלקה תקפות גם למחלקה יורשת. אך ניתן לשנותם באופן מבוקר. נניח שמחלקה C היא לקוחה של מחלקה A, כלומר: יש ל- C תכונה מטיפוס A או שאחת הפעולות של C מקבלת פרמטר מטיפוס A העצם המקושר לתכונה/פרמטר יכול להיות ממחלקה B שיורשת מ A אבל C מצפה שהעצם יקיים את החוזה של A 34

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

  36. תנאי קדם ואחר וירושה נניח שב A מוגדרת פעולה f והמחלקה היורשת הגדירה אותה מחדש (דרסה אותה) נניח שמ C יש קריאה מהצורה a.f(..)כש a הוגדר מטיפוס A , ובזמן ריצה a מקושר לעצם מטיפוס B C דאגה לקיים את תנאי הקדם ש A.f(..) לכן אסור ש B.f(..) תדרוש משהו חזק יותר תנאי הקדם במחלקה היורשת שווה או חלש מהמקורי C מצפה שאחרי סיום הפעולה יתקיים תנאי הבתר של A.f(..) ולכן B.f(..) מחויבת לספק זאת תנאי האחר במחלקה היורשת שווה או חזק מהמקורי 36

  37. דוגמא public class MATRIX { ... /** inverse of current with precision epsilon * @pre epsilon >= 10 ^(-6) * @post (this.mult($prev(this)) – ONE).norm <= epsilon */ void invert(double epsilon); ... } עיצוב בעזרת חוזים

  38. דוגמא public class ACCURATE_MATRIX extends MATRIX { ... /** inverse of current with precision epsilon * @pre epsilon >= 10^(-20) * @post (this.mult($prev(this)) – ONE).norm <= epsilon/2 */ void invert(double epsilon); ... } עיצוב בעזרת חוזים

  39. ירושה וחריגים • משהבנו את ההיגיון שבבסיס יחסי ספק, לקוח וקבלן משנה, ניתן להסביר את חוקי שפת Java הנוגעים לחריגים ולירושה • קבלן משנה (מחלקה יורשת [מממשת], הדורסת [מממשת] שרות) אינו יכול לזרוק מאחורי הקלעים חריג שלא הוגדר בשרות הנדרס [או במנשק] • למתודה הדורסת [המממשת] מותר להקל על הלקוח ולזרוק פחות חריגים מהמתודה במחלקת הבסיס שלה [במנשק] עיצוב בעזרת חוזים

  40. עוד על ירושה וחוזים • בנוסף לחריגים, שלגביהם ג'אוה מקפידה על כללי החוזה בירושה, יש עוד כללים בשפה שנובעים משיקולי חוזה וירושה: • למתודה הדורסת [המממשת] מותר להקל את הנראות – כלומר להגדיר סטטוס נראות רחב יותר, אבל אסור להגדיר סטטוס נראות מצומצם יותר. • (מגירסא 5) למתודה הדורסת [המממשת] מותר לצמצם את טיפוס הערך המוחזר, כלומר טיפוס הערך המוחזר הוא תת טיפוס של טיפוס הערך המוחזר במתודה במחלקת הבסיס שלה [במנשק]. (הגדרה מחדש קו-וריאנטית) עיצוב בעזרת חוזים

  41. הגדרת תנאי בתר בירושה - תיקון בכללים שהצגנו יש אי דיוק – הם מגבילים מדי אם תנאי הקדם הוא Pre ותנאי הבתר הוא Post , אז תנאי הבתר האפקטיבי הוא $prev(Pre) implies Post תנאי האחר האפקטיבי הוא זה שצריך להיות שווה או חזק, אחרת זה לא מאפשר להגדיר את ההתנהגות של הפונקציה עבור המקרה "החדש" (שנוסף לתנאי הקדם). דוגמא – פונקציה לחישוב שורש x עם תנאי קדם ש x חיובי. בירושה רוצים להחליש את תנאי הקדם ולאפשר x שלילי, ולהגדיר את תנאי הבתר שיחזיר 0 כש x שלילי. זה לא חוקי לפי הכללים המקוריים בגירסאות מאוחרות של אייפל תיקנו את זה בשפות אחרות מגדירים לכל פונקציה זוגות של תנאי קדם ותנאי בתר מתאים, וניתן להוסיף זוגות במחלקה היורשת עיצוב בעזרת חוזים

  42. תמיכת כלי תוכנה בחוזים

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

  44. תכנות ע"י חוזה באייפל מספר מרכיבים תחביריים (אופציונליים) בשפה: תנאי קדם: require אחרי כותרת המתודה, לפני הגוף תנאי אחר: ensure לפני סוף גוף המתודה משתמר: invariant לפני סוף המחלקה אוסף ביטויים בוליאניים (מצורפים ב and אימפליציטי), עם תווית אופציונלית. בתנאי אחר יכול להופיע האופרטור old – ערך הביטוי בכניסה למתודה הפסאודו משתנה Result – הערך המוחזר תמיכה בתכנות ע"י חוזים בירושה (קבלן משנה), ותחביר מיוחד לשינויים במחלקה היורשת: require else ו ensure then דוגמא -- מחסנית (מחלקה גנרית) עיצוב בעזרת חוזים

  45. דוגמא לחוזה באייפל - מחסנית עיצוב בעזרת חוזים class STACK [T] creation make feature -- Initialization make(n: INTEGER) is -- Allocate stack for maximum of n elements, require positive_capacity: n >= 0 do capacity:=n !!representation(1,capacity) ensure capacity_set: capacity = n array_allocated: representation /= Void stack_empty: empty end

  46. המשך הדוגמא עיצוב בעזרת חוזים feature -- Access capacity: INTEGER -- The maximum number of stack elements count: INTEGER -- Number of stack elements top: T is -- Top element require not_empty: not empty -- i.e. count > 0 do Result:=representation @ count end

  47. המשך הדוגמא עיצוב בעזרת חוזים feature -- Status report empty: BOOLEAN is -- Is stack empty? do Result:=(count = 0) ensure empty_definition: Result=(count = 0) end full: BOOLEAN is -- Is stack full? do Result:=(count = capacity) ensure full_definition: Result=(count=capacity) end

  48. המשך הדוגמא עיצוב בעזרת חוזים feature -- Element change push(x: T) is -- Add x on top require not_full: not full do count:=count+1 representation.put(count,x) ensure not_empty: not empty added_to_top: top = x one_more_item: count = old count+1 in_top_array_entry: representation @ count =x end

  49. המשך הדוגמא עיצוב בעזרת חוזים pop is -- Remove top element require not_empty: not empty do count:=count-1 ensure not_full: not full one_fewer: count = old count-1 end feature {NONE}-- Implementation representation: ARRAY[T] -- The array used to hold elements

  50. המשך הדוגמא עיצוב בעזרת חוזים Invariant -- of STACK count_non_negative: 0 <= count count_bounded: count <= capacity consistent_with_array_size: capacity= representation.capacity empty_if_no_elements: empty = (count = 0) item_at_top: (count > 0) implies representation.item(count)=top end -- class STACK

More Related