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

Preview:

DESCRIPTION

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

Citation preview

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

עמירם יהודאי

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

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

על סדר היום

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

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

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

מוטיבציה

מערכת תוכנה

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

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

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

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

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

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

5

API

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

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

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

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

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

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

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

הגדרת השאלה

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

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

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

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

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

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

פערי הבנה

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

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

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

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

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

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

2ו-

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

חוזים

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

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

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

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

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

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

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

12

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

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

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

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

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

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

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

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

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

החוזה והמצב

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

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

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

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

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

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

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

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

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

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

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

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

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

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

חלוקת אחריות

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

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

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

התקיים

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

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

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

דוגמא/** * @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

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

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

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

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

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

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

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

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

משתמר

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

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

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

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

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

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

משתמר הייצוג

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

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

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

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

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

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

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

משתמר הייצוג

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

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

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

/** @pre !isEmpty)(

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

*/

public void pop)( {

top--;

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

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

דע מה אתה מבקש

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

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

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

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

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

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

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

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

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

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

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

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

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

בהמשך(

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

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

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

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

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

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

34

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

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

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

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

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

35

משתמר וירושה

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

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

36

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

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

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

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

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

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

דוגמא

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

דוגמא

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

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

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

ולירושה

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

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

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

[במנשק]

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

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

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

וירושה:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

פקודת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

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

from initialization_instructions -- optional

invariant invariant -- optional

variant variant -- optional

until exit_condition

loop loop_instructions

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

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

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

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

תיעוד החוזים

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

JML - The Java Modeling Language

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

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

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

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

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

JML המשך

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

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

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

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

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

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

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

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

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

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

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

Spec#

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

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

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

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

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[]

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

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

סיכום

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

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

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

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

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