design by contract n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
עיצוב בעזרת חוזים Design by Contract PowerPoint Presentation
Download Presentation
עיצוב בעזרת חוזים Design by Contract

Loading in 2 Seconds...

play fullscreen
1 / 71

עיצוב בעזרת חוזים Design by Contract - PowerPoint PPT Presentation


  • 110 Views
  • Uploaded on

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

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'עיצוב בעזרת חוזים Design by Contract' - sahara


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.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
design by contract

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

עמירם יהודאי

אוניברסיטת תל אביב

סמינר מורים מובילים קיץ 2009

slide2
על סדר היום
  • מוטיבציה
  • חוזים
  • ירושה וחוזים
  • תמיכת כלי תוכנה בחוזים
  • כלים מתקדמים וכיווני מחקר

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

slide5
API

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

חתימה של פעולה: השם, טיפוסי הפרמטרים, וטיפוס הערך המוחזר

לדוגמא, בג'אוה מקובל להשתמש ב javadoc, כלי תוכנה שמחולל תיעוד אוטומטי בפורמט html על בסיס הערות התיעוד שהופיעו בגוף קובצי המקור

תיעוד זה מכונה API (Application Programming Interface)

מעכשו נתייחס לתכנות מונחה עצמים: מחלקות הן המודולים

חלק מהעקרונות רלבנטיים גם לתכנות פרוצדורלי ופונקציונלי

עיצוב בעזרת חוזים

5

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

עיצוב בעזרת חוזים

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

publicstaticint divide(int numerator,

int denominator)

{...}

    • לרוב הקוראים יש מושג כללי נכון לגבי הפונקציה ופעולתה
    • למשל, ברור מה תחזיר הפונקציה אם נקרא לה עם הארגומנטים 6 ו- 2

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

design by contract2
עיצוב על פי חוזה (design by contract)

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

תנאיקדם (precondition) – מהן ההנחות של כותב הפונקציה לגבי הדרך התקינה להשתמש בה

תנאי בתר (תנאי אחר, postcondition) – מה עושה הפונקציה, בכל אחד מהשימושים התקינים שלה

נשתדל לתאר את תנאי הקדם ותנאי הבתר במונחים של ביטויים בולאנים חוקיים ככל שניתן (לא תמיד ניתן)

שימוש בביטויים בולאניים חוקיים (נקרא להם טענות):

מדויק יותר

יאפשר לנו בעתיד לאכוף את החוזה בעזרת כלי חיצוני

עיצוב בעזרת חוזים

12

divide
חוזה אפשרי ל- 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 מציין את הערך שהפונקציה מחזירה
  • לפעמים החוזה ארוך יותר מגוף הפונקציה

עיצוב בעזרת חוזים

divide1
חוזה אפשרי אחר ל- 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 הוא האופרטור גרירה לוגית

עיצוב בעזרת חוזים

תנאי קדם סובלניים מסבכים את מימוש הפונקציה – כפי שמתבטא בחוזה

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

עיצוב בעזרת חוזים

slide16
/** @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

slide17
/** @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

slide18
public boolean isFull() {

return top == max;

}

/**

@post ($ret == (top == 0))

*/

public boolean isEmpty() {

return top == 0;

}

}

הערך המוחזר

עיצוב בעזרת חוזים

18

slide19
/** @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

slide20
/** @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

slide21
public boolean isFull() {

return top == max;

}

/**

@post ($ret == (top == 0))

*/

public boolean isEmpty() {

return top == 0;

}

}

גירסא גנרית

הערך המוחזר

עיצוב בעזרת חוזים

21

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

slide24
דוגמא

/**

*@paramaAnarraysortedinascendingorder

*@paramxanumbertobesearchedina

*@returnthefirstoccurrenceofxina, or -1 if

* it x does not occur in a

*

*@pre"aissortedinascendingorder"

*/

publicstaticint searchSorted(int [] a, int x)

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

slide28
משתמר הייצוג
  • משתמר ייצוג (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;

.....

}

  • הבחנה במשתמר ייצוג – ע"י כלי, או תיוג שונה

עיצוב בעזרת חוזים

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

/** @pre !isEmpty()

@post (top == $prev (top) - 1)

*/

public void pop() {

top--;

}

  • אבל לא בתנאי קדם של מתודות ציבוריות
  • מדוע?

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

slide31
האם זה מזכיר לכם משהו?
  • פקודת assert קיימת במספר שפות

assert <boolean-expression>

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

slide34
תכנות ע"י חוזה וירושה

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

נניח שמחלקה C היא לקוחה של מחלקה A, כלומר:

יש ל- C תכונה מטיפוס A

או שאחת הפעולות של C מקבלת פרמטר מטיפוס A

העצם המקושר לתכונה/פרמטר יכול להיות ממחלקה B שיורשת מ A

אבל C מצפה שהעצם יקיים את החוזה של A

34

slide35
משתמר וירושה

המחלקה B חייבת לקיים את האינורינטה של ההורה.

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

35

slide36
תנאי קדם ואחר וירושה

נניח שב A מוגדרת פעולה f והמחלקה היורשת הגדירה אותה מחדש (דרסה אותה)

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

C דאגה לקיים את תנאי הקדם ש A.f(..) לכן אסור ש B.f(..) תדרוש משהו חזק יותר

תנאי הקדם במחלקה היורשת שווה או חלש מהמקורי

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

תנאי האחר במחלקה היורשת שווה או חזק מהמקורי

36

slide37
דוגמא

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);

...

}

עיצוב בעזרת חוזים

slide38
דוגמא

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);

...

}

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

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

עיצוב בעזרת חוזים

slide41
הגדרת תנאי בתר בירושה - תיקון

בכללים שהצגנו יש אי דיוק – הם מגבילים מדי

אם תנאי הקדם הוא Pre ותנאי הבתר הוא Post , אז תנאי הבתר האפקטיבי הוא $prev(Pre) implies Post

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

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

בגירסאות מאוחרות של אייפל תיקנו את זה

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

עיצוב בעזרת חוזים

slide43
תכנות ע"י חוזה באייפל

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

באייפל החוזים הם חלק מהשפה

תמיכת השפה בחוזים כוללת

תיעוד

מעקב בזמן ריצה

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

עיצוב בעזרת חוזים

slide44
תכנות ע"י חוזה באייפל

מספר מרכיבים תחביריים (אופציונליים) בשפה:

תנאי קדם: require אחרי כותרת המתודה, לפני הגוף

תנאי אחר: ensure לפני סוף גוף המתודה

משתמר: invariant לפני סוף המחלקה

אוסף ביטויים בוליאניים (מצורפים ב and אימפליציטי), עם תווית אופציונלית.

בתנאי אחר יכול להופיע

האופרטור old – ערך הביטוי בכניסה למתודה

הפסאודו משתנה Result – הערך המוחזר

תמיכה בתכנות ע"י חוזים בירושה (קבלן משנה), ותחביר מיוחד לשינויים במחלקה היורשת: require else ו ensure then

דוגמא -- מחסנית (מחלקה גנרית)

עיצוב בעזרת חוזים

slide45

דוגמא לחוזה באייפל - מחסנית

עיצוב בעזרת חוזים

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

slide46

המשך הדוגמא

עיצוב בעזרת חוזים

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

slide47

המשך הדוגמא

עיצוב בעזרת חוזים

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

slide48

המשך הדוגמא

עיצוב בעזרת חוזים

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

slide49

המשך הדוגמא

עיצוב בעזרת חוזים

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

slide50

המשך הדוגמא

עיצוב בעזרת חוזים

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

slide51
עוד סוגי טענות באייפל

פקודת check דומה לפקודת assert בשפות אחרות

checkassertion1; assertion2; .. end

הטענה תתקיים כל פעם שמגיעים לנקודה זאת בקוד. לדוגמא:

x:=a*a + b*b

check x >=0 end

לדוגמא במתודה pushבגירסא אחרת של מחסנית :

if full then

error:=Overflow

else

check representation /= Void end

representation.push(x)

error:=0

end

עיצוב בעזרת חוזים

slide52
אינוריאנטה ווריאנט של לולאות

frominitialization_instructions-- optional

invariant invariant -- optional

variantvariant -- optional

untilexit_condition

looploop_instructions

end

חלק האיתחול הוא אופציונלי, וכך גם האינוריאנטה והוריאנט

האינוריאנטה מתקיימת לפני כל ביצוע של גוף הלולאה.

הוריאנט תמיד שלם אי שלילי, וקטן כל איטרציה.

עיצוב בעזרת חוזים

slide53

דוגמא: חישוב GCD

עיצוב בעזרת חוזים

gcd(a,b: INTEGER): INTEGER is

-- Greatest common divisor of a and b

require

a>0; b>0

local x, y : INTEGER

do

…. -- see next slide

ensure

-- Result is the gcd of a and b

end

slide54

המשך GCD עם אינוריאנטה ווריאנט

עיצוב בעזרת חוזים

from

x:=a; y:=b

invariant

x>0;y>0 --(x,y) have same GCD as (a,b)

variant

x.max(y)

until

x=y

loop

if x>y then x:=x-y

else y:=y-x end

end

slide55
תיעוד החוזים

הכלי short להפשטה של מחלקה.

יוצר תיעוד מהקוד.

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

לא כולל גוף של מתודות do ...

פורמט שונה ממחלקה, למנוע אי הבנות.

בג'אוה -- כלי Javadoc ליצירת תיעוד ממחלקה, אפשר להגדיר לכלי את התגיות המיוחדות לחוזים

[הערה – החל מג'אוה 5 ניתן להשתמש באנוטציות (annotations) במקום הערות ]

עיצוב בעזרת חוזים

slide56
מעקב בזמן ריצה

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

נכתב בקובץ קונפיגורציה.

הקומפילר מבצע אינסטרומנטציה של הקוד

מוסיף פקודות לבדיקת הטענות וזריקת exception

כל רמה כוללת את הקודמות:

בלי בדיקה.

תנאי קדם (ברירת המחדל).

תנאי בתר

אינוריאנטות

לולאות

הכל

עיצוב בעזרת חוזים

slide57
מעקב בזמן ריצה - דיון

בטיחות לעומת יעילות

שלב ניפוי שגיאות לעומת שלב production .

בדומה לבדיקת גבולות מערך בגישה לאבר.

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

זמן ריצה גדול בכ %50

מטפל ברוב התקלות הכבדות

מערכת זמן הריצה מדפיסה הודעה על המחלקה ואיזו טענה הופרה.

עיצוב בעזרת חוזים

slide58
שימוש בטענות - דיון

מעקב בזמן ריצה לעומת אימות.

יכולת ביטוי מוגבלת: לא כל תכונה ניתנת לביטוי בשפה המוגבלת של הטענות.

אין כמתים (למשל, לכל)

זה חלק מהגישה הבסיסית:

פשרה בין אפיון מלא למחיר סביר של בדיקתו

לדוגמא: במחסנית, לכאורה תנאי האחר של push צריך לבטא שכל האברים הקיימים לא השתנו

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

עיצוב בעזרת חוזים

slide59
הערות נוספות על שימוש בטענות

קריאה לפעולה מטענה: (כמו בדוגמת המחסנית)

בלי תופעות לוואי (כלומר שאילתא)

חלק מההצדקה להפרדה בין שאילתות לפקודות

האם יש בטחון בנכונות הפונקציה?

פונקציות שנקראות מטענות מבוקרות מבוצעות ללא מעקב על הטענות שלהן.

בעייה: עקב שיתוף רפרנסים תתכן הפרת אינוריאנטה מהחוץ.

עיצוב בעזרת חוזים

59

slide60
מה קרה אחרי אייפל

חוזה הפך למושג רווח אצל מפתחים וחוקרים

לא רק במובן הפורמלי, לא רק עם כלי תוכנה

גם בהוראת תכנות מונחה עצמים

עבור שפות תכנות אחרות, שלא כוללות חוזים (למשל ג'אוה, #C) פותחו כלים שיאפשר את מה שנעשה באייפל

גם ב UML יש מרכיב שנועד להגדרת חוזים, שפה שנקראתOCL (Object Constraint Language)

פעילות מחקרית נרחבת להרחיב את הגישה ואת יכולותיה

slide61
תכנות ע"י חוזה בג'אוה ו#C

פותחו מספר כלים המאפשרים לשלב את עקרונות תכנות ע"י חוזה בשפת ג'אוה. רשימה חלקית:

iContract2, Contract4J, jContractorJMSAssert

כנ"ל ל # CCode Contracts

החוזה נכתב כהערות במבנה מוגדר

דוגמת המחסנית בתחילת ההרצאה נלקחה מ JMSAssert

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

המתכנת בוחר אחרי איזה מחלקות, ואיזה טענות לעקוב

כלים מתקדמים משלבים גם ניתוח סטטי (שימוש בכלים להוכחת נכונות).

עיצוב בעזרת חוזים

jml the java modeling language
JML - The Java Modeling Language

עיצוב בעזרת חוזים

63

שפה לאפיון מחלקות בג'אוה

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

כוללת תנאי קדם, בתר ומשתמרים

אפשרות להגדיר משתנים שאינם null

כמתים, למשל

(\forall Student s; juniors.contains(s); s.getAdvisor ( ) != null)

לכל הסטודנטים שמוכלים ב juniorsיש מנחה

טענה הקובעת איזה תכונות משתנות ע"י הפעולה, לדוגמא assignable x , y

slide64
JML המשך

עיצוב בעזרת חוזים

64

64

  • טענה שלפעולה אין תופעות לוואיpure
    • רק אז הפעולה יכולה להופיע כחלק מטענה
  • אפשרות להפריד תנאי בתר רגילים ותנאי בתר חריגים
  • אפשרות למספר זוגות תנאי קדם/תנאי אחר
  • כללים לגבי ירושה בדומה למה שראינו
  • טענות יכולות להיות פרטיות אן ציבוריות
    • תכונה או פעולה פרטית לא תופיע בטענה ציבורית
  • אפשרות להגדיר תכונות מודל, שאינן חלק מהמימוש ונועדו לצורך כתיבת האפיון
slide65
דוגמא

public class Person{

/*@ normal_behavior

@ requires kgs >= 0;

@ assignable weight;

@ ensures weight == \old(weight) + kgs;

@ also

@ exceptional_behavior

@ requires kgs < 0;

@ assignable \nothing;

@ signals (Exception e)

(e instanceof IllegalArgumentException);

@*/

public void addKgs(int kgs) {

if (kgs >= 0) {

weight += kgs;

} else {

throw new IllegalArgumentException();

}

}

}

slide66
כלים לתמיכה ב JML

עיצוב בעזרת חוזים

66

66

  • קומפילר למעקב בזמן ריצה (מבצע אינסטרומנטציה של הקוד), בדומה לאייפל
  • כלי לבדיקת תקינות טיפוסים
  • כלי לבדיקות
  • כלי לתיעוד
  • ניתוח סטטי – כלי בשם ESC/Java
    • משתמש בכלי להוכחת משפטים,
    • מגלה שגיאות כגון דירפרנס ל null
  • פלטפורמה להמשך מחקר ופיתוח כלים
slide67
Spec#

עיצוב בעזרת חוזים

67

שפה פורמלית לחוזי API

פותחה במעבדות המחקר של מיקרוסופט

הושפעה ע"י JML , אייפל וכו'

הרחבה של #C

כוללת מרכיבים דומים לאלה של JML

העיקר – כלי אימות סטטי בוגי, שפותח אף הוא במיקרוסופט

אימות אוטומטי או אינטראקטיבי (סיוע של המתכנת)

spec architecture
Spec# Architecture

Boogie

ByteCode

Translation

Spec# Compiler

Boogie

Source-Level

Analysis

ByteCode

Generation

Invariant

inference

Verification

Condition

generation

Theorem

proving

Spec#

Source

code

Intermediate

Language

Boogie

PL

Boogie

PL with

Invariants

First-Order

Formula

“correct” or list of errors

non null types back to example
Non-Null Types – back to example

Was

Int[]

class MyNonNullLibrary

{

public static void Clear(int[]! xs)

{

for (int i = 0; i < xs.Length; i++)

{

xs[i] = 0;

}

}

}

class ClientCode

{

static void Main()

{

int[] xs = null;

MyNonNullLibrary.Clear(xs);

}

}

slide70
פעילות מחקרית נוספת

הסקה חצי אוטומטית של חוזים מתוך הקוד

ריפקטורינג של חוזים לפני שינוי קוד

שימוש בחוזים ליצירת טסטים

שיפור שיטות האימות הסטטי, וכלים נסיוניים רבים

......

slide71
סיכום

חוזים הם אמצעי לשילוב אפיון (חלקי) של מחלקות בתוך הקוד

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

מעקב ריצת התכנית עוזרת לניפוי שגיאות (דינמי)

בעתיד נראה שפות וכלים לבדיקה סטטית של חוזים

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