71
םםםםם םםםםם םםםםםDesign by Contract ייייי יייייי יייייייייי יי יייי ייייי ייייי ייייייי ייי2009

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

  • Upload
    sahara

  • View
    42

  • Download
    5

Embed Size (px)

DESCRIPTION

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

Citation preview

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

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

עמירם יהודאי

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

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

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

על סדר היום

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

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

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

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

מוטיבציה

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

מערכת תוכנה

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

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

לצורך הפרדת עניינים(separation of concern) ,כותבי מודול צריכים לדעת רק מה שנחוץ על

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

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

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

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

5

API

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

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

המוחזר בג'אוה מקובל להשתמש ב לדוגמא ,javadoc , כלי תוכנה

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

תיעוד זה מכונהAPI )Application Programming Interface(מעכשו נתייחס לתכנות מונחה עצמים: מחלקות הן המודולים

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

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

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

הגדרת השאלה

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

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

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

מהספקאיזה מידע על מחלקת הספק יש להעמיד שאלה :

לרשות מפתחת מחלקת הלקוח? במילים אחרות: מה מכיל הAPI?של הספק ?ובמיוחד: האם חתימה + תאור מילולי מספיקים

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

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

פערי הבנה

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

שונים עשויים להיות פערי הבנה לגבי תפקוד שרות מסוים ,הפערים נובעים ממגבלות השפה הטבעית, פערי תרבות

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

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

public static int divide(int numerator, int denominator) {...}

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

2ו-

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

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

?מה קורה במקרים אחרים

2 ו- 7אך מה יוחזר עבור הארגומנטים? ?האם הפונקציה מעגלת למעלה? מעגלת למטה?ועבור ערכים שליליים?אולי היא מעגלת לפי השלם הקרוב

בעבור מספרים שאינם אסורואולי השימוש בפונקציה מתחלקים ללא שארית?

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

כאשר המכנה הוא אפס?אסורואולי השימוש בפונקציה בפונקציה?אסורמה קורה בעקבות שימוש

תעוףהאם התוכנית? אם כן, איזה?ערך שגיאההאם מוחזר ? האם קיים משתנה או מנגנון שבאמצעותו ניתן לעקוב אחרי שגיאות

שארעו בתוכנית?עיצוב בעזרת חוזים8

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

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

divideלפעול :ואולם יש לציין במפורש

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

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

של החוזהפרוט ההנחות וההתנהגויות השונות מכונה הפונקציה

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

...'קבלן ודיירים, מוכר וקונים, מלון ואורחים וכו

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

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

חוזים

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

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

,הגישה הוצעה על ידי מפתח שפת התכנות אייפל(Bertrand Meyer)ברטראן מאייר

מבוססת על עבודות שלHoare ו Floyd אייפל כוללת תחביר מיוחד להגדרת חוזים כחלק

מהשפה אנחנו נציג את הגישה בשפתJava שלא כוללת תחביר ,

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

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

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

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

12

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

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

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

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

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

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

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

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

divideחוזה אפשרי ל-

/** * @pre denominator != 0 , * “Can’t divide by zero" * * @post Math.abs($ret * denominator) <= Math.abs(numerator) , * "always truncates the fraction" * * @post (($ret * denominator) + (numerator % denominator)) * == numerator, "regular divide" */public static int divide(int numerator, int denominator)

$retמציין את הערך שהפונקציה מחזירה לפעמים החוזה ארוך יותר מגוף הפונקציה

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

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

divide ל- אחרחוזה אפשרי /** * @pre (denominator != 0) || (numerator != 0) , * "you can't divide zero by zero" * * @post (denominator == 0) && ((numerator > 0)) * $implies $ret == Integer.MAX_VALUE * "Dividing positive by zero yields infinity (MAX_INT)" * * @post (denominator == 0) && ((numerator < 0)) $implies * $ret == Integer.MIN_VALUE * "Dividing negative by zero yields minus infinity (MIN_INT)" * * @post Math.abs($ret * denominator) <= Math.abs(numerator) , * "always truncates the fraction" * * @post (denominator != 0) $implies * (($ret * denominator)+(numerator % denominator)) == numerator, * "regular divide" */public static int divide(int numerator, int denominator)

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

$impliesהוא האופרטור גרירה לוגית

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

החוזה והמצב

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

קריאות לשאילתות( שרק בו ניתן לקרוא לפעולה לדוגמא: במחלקהMyStack ניתן לקרוא לפעולה

pop .רק אם המחסנית אינה ריקה נשים לב שמימוש הפעולות מתעלם לחלוטין

מהמקרים שבהם תנאי הקדם אינו מתקיים המימוש לא בודק את תנאי הקדם בגוף המתודה)נחזור לאינוריאנטה בהמשך(

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

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

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

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

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

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

18

public boolean isFull)( {

return top == max;

}

/**

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

*/

public boolean isEmpty)( {

return top == 0;

}

}

הערך המוחזר

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

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

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

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

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

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

21

public boolean isFull)( {

return top == max;

}

/**

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

*/

public boolean isEmpty)( {

return top == 0;

}

}

הערך המוחזר

גירסא גנרית

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

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

פעולה לעולם לא תבדוק את תנאי הקדם שלה

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

אזי יש לה התנהגות מוגדרת היטב עבור אותו תנאי – כלומר הוא אינו תנאי קדם עוד

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

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

"גישת תיכון ע"פ חוזה סותרת גישה בשם "תכנות מתגונן(defensive programmingשעיקריה לבדוק תמיד הכל )

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

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

חלוקת אחריות

?אבל מה אם הלקוח שכח לבדוק!זו הבעיה שלו

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

)אחרת הספק לא מחויב לדבר( הספק – מתחייב למילוי כל תנאי האחר אם תנאי הקדם

התקיים

הצד השני של המטבע – לאחר קריאה לשרות אין צורךלבדוק שהשרות בוצע.

...ואם הוא לא בוצע? יש לנו את מי להאשים

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

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

דוגמא/** * @param a An array sorted in ascending order * @param x a number to be searched in a * @return the first occurrence of x in a, or -1 if * it x does not occur in a * * @pre "a is sorted in ascending order" */public static int searchSorted(int [] a, int x)

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

1להחזיר? -?למיין את המערך?לחפש במערך הלא ממוין

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

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

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

חיזוק תנאי האחר

אם תנאי הקדם לא מתקיים, לפעולה מותר שלא לקיים אתתנאי האחר כשהיא מסיימת; קריאה לשירות כאשר תנאי

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

,לפעולה לפעול מותראבל גם אם תנאי הקדם לא מתקיים ולקיים את תנאי האחר

לפעולה מותר גם לייצר כאשר היא מסיימת מצב הרבה יותרספציפי מזה המתואר בתנאי האחר; תנאי האחר לא חייב

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

למשל, פעולה המתחייבת לביצוע חישוב בדיוק של כלשהו / 2יכולה בפועל להחזיר חישוב בדיוק של

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

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

משתמר

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

מצב העצםשמורה, משתמר( invariant הוא ביטוי בוליאני שערכו נכון – )

מיד לאחר יצירת העצם, ולפני ואחרי כל פעולה )ציבורית!(ראינו בדוגמא של המחסנית

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

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

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

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

משתמר הייצוג

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

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

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

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

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

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

שאילתות(עיצוב בעזרת חוזים27

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

משתמר הייצוג

( משתמר ייצוג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; .....}

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

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

תנאי בתר ייצוגי

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

/** @pre !isEmpty)(

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

*/

public void pop)( {

top--;

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

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

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

דע מה אתה מבקש

?מי מונע מאיתנו לעשות שטויותאף אחד

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

כךפרטים בהמשך

השימוש בהם עדיין לא נפוץ מספיק אולם, לציון החוזה )אפילו כהערה!( חשיבות

מתודולוגית נכבדה בתהליך תכנון ופיתוח מערכות תוכנה גדולות

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

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

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

האם זה מזכיר לכם משהו?

פקודת assertבמספר שפותמת קיי assert <boolean-expression>

המשתמש מפעיל את הפקודות האלה ע"י אופציה שלהקומפילר

הבוליאניהביטוי > boolean-expression< מחושב אם ערכוtrue לא קורה שום דבר אם ערכוfalse מורם חריג( exception).עם הודעה מתאימה ,

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

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

בהמשך(

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

החוזה והקומפיילר

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

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

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

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

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

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

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

34

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

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

נניח שמחלקהC היא לקוחה של מחלקה A:כלומר , -יש לC תכונה מטיפוס A או שאחת הפעולות שלC מקבלת פרמטר מטיפוס A

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

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

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

35

משתמר וירושה

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

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

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

36

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

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

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

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

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

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

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

דוגמא

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

...

}

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

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

דוגמא

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

...}

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

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

ירושה וחריגים

משהבנו את ההיגיון שבבסיס יחסי ספק, לקוח וקבלן הנוגעים לחריגים Javaמשנה, ניתן להסביר את חוקי שפת

ולירושה

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

בשרות הנדרס [או במנשק]

[המממשת] על הלקוח מותר להקללמתודה הדורסת חריגים מהמתודה במחלקת הבסיס שלה פחותולזרוק

[במנשק]

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

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

עוד על ירושה וחוזים

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

וירושה:

[המממשת] את הנראות – מותר להקללמתודה הדורסת כלומר להגדיר סטטוס נראות רחב יותר, אבל אסור להגדיר

סטטוס נראות מצומצם יותר.

מותר לצמצם( למתודה הדורסת [המממשת] 5)מגירסא את טיפוס הערך המוחזר, כלומר טיפוס הערך המוחזר הוא

תת טיפוס של טיפוס הערך המוחזר במתודה במחלקת הבסיס שלה [במנשק]. )הגדרה מחדש קו-וריאנטית(

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

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

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

בכללים שהצגנו יש אי דיוק – הם מגבילים מדי אם תנאי הקדם הואPre ותנאי הבתר הוא Post אז תנאי ,

prev)Pre( implies Post$ הוא האפקטיביהבתר ,תנאי האחר האפקטיבי הוא זה שצריך להיות שווה או חזק

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

המשך הדוגמא

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

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

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

המשך הדוגמא

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

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

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

המשך הדוגמא

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

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

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

המשך הדוגמא

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

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

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

המשך הדוגמא

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

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

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

פקודתcheck דומה לפקודת assert בשפות אחרות check assertion1; 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

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

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

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

from initialization_instructions -- optional

invariant invariant -- optional

variant variant -- optional

until exit_condition

loop loop_instructions

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

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

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

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

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

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

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

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

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

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

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

תיעוד החוזים

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

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

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

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

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

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

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

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

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

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

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

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

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

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

% 50 זמן ריצה גדול בכ מטפל ברוב התקלות הכבדות

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

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

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

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

.מעקב בזמן ריצה לעומת אימות יכולת ביטוי מוגבלת: לא כל תכונה ניתנת לביטוי

בשפה המוגבלת של הטענות.)אין כמתים )למשל, לכל

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

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

לחסוך מעקב: גישות וכלים מתקדמים יותרעיצוב בעזרת חוזים58

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

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

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

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

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

אינוריאנטה מהחוץ.

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

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

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

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

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

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

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

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

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

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

iContract2, Contract4J, jContractor JMSAssert# כנ"ל ל C Code Contractsהחוזה נכתב כהערות במבנה מוגדר דוגמת המחסנית בתחילת ההרצאה נלקחה מJMSAssert הכלי מבצע אינסטרומנטציה של הקוד כדי לעקוב אחרי

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

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

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

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

כלים מתקדמים וכיווני מחקר

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

JML - The Java Modeling Language

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

קודמות, לשפות מוקדמות יותרכוללת תנאי קדם, בתר ומשתמרים אפשרות להגדיר משתנים שאינםnullכמתים, למשל

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

יש מנחה juniorsלכל הסטודנטים שמוכלים ב ,טענה הקובעת איזה תכונות משתנות ע"י הפעולה

assignable x , yלדוגמא עיצוב בעזרת חוזים6363

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

JML המשך

טענה שלפעולה אין תופעות לוואי pure רק אז הפעולה יכולה להופיע כחלק מטענה

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

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

ונועדו לצורך כתיבת האפיון

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

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

דוגמא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)(; } }}

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

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

קומפילר למעקב בזמן ריצה )מבצע אינסטרומנטציהשל הקוד(, בדומה לאייפל

כלי לבדיקת תקינות טיפוסיםכלי לבדיקותכלי לתיעוד ניתוח סטטי – כלי בשםESC/Java

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

פלטפורמה להמשך מחקר ופיתוח כליםעיצוב בעזרת חוזים6666

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

Spec#

שפה פורמלית לחוזיAPIפותחה במעבדות המחקר של מיקרוסופט הושפעה ע"יJML'אייפל וכו , # הרחבה שלC כוללת מרכיבים דומים לאלה שלJML העיקר – כלי אימות סטטי בוגי, שפותח אף הוא

במיקרוסופט אימות אוטומטי או אינטראקטיבי )סיוע של

המתכנת(עיצוב בעזרת חוזים6767

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

Spec# Architecture

Source-LevelAnalysis

ByteCodeGeneration

Invariant inference

VerificationCondition

generation

Theoremproving

Spec#Source code

IntermediateLanguage

BoogiePL

BoogiePL with

Invariants

First-OrderFormula

BoogieBoogie

ByteCodeTranslation

Spec# Compiler

“correct” or list of errors

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

Non-Null Types – back to example

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

Was

Int[]

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

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

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

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

סיכום

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

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

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

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

כמטפורה לתכנות טוב