תכ נות מת קדם בש פת Java עיצו ב מחל קה (וקצת עיצוב על פי חוזה) אורנית דרור ואוהד ברזי לי 1
תכנון תוכנה למערכת בנ קאית תכנון מערכת תוכנה עוסק במיפוי בין עולם הבעי ה ועולם הפתרון עו לם הפתרון: שפת תכנות עצמים מחלק ות מתודות שדות עו לם הבעיה: בנ קים לקוחו ת משיכות, הפ קדו ת חשבונות יתרות 2
מח לקה לייצוג ח שבון בנ ק בגישה מוכוונת עצמים כל שם עצם מעולם הבעיה הוא מועמד לייצוג ע"י מחלקה נתכנן מחלקה,,BankAccount לייצוג חשבון בנק ננסה להפוך את התאור המילולי והתפיסה האינ טואיטיבית שלנו של חשב ון בנק לרכיב תוכנה תאור הפעולות יתבטא בחוז ה ובמתודות המחלק ה לה ז ה ר לא יש (דוג מא: להצמד יותר מדי פקיד בנק שעושה הכל) לתאור העולם האמיתי 3
המ צב הפנימי המצ ב הפנימי של ע צם מיוצג ע"י נתוניו (שדותיו) שדות ע צם הם בד"כ עם הרשאת גישה פרטית במק רה של חשבון בנק היתרה מאיזה טיפוס? public class BankAccount {... private??? balance; 4
המ צב הפנימי המצ ב הפנימי של ע צם מיוצג ע"י נתוניו (שדותיו) שדות ע צם הם בד"כ עם הרשאת גישה פרטית במק רה של חשבון בנק היתרה מאיזה טיפוס? public class BankAccount {... private double balance; 5
שרותי מ חלקה 6 ישנם 3 סוגים של מתודות פקודות (שרותים, פונקציות): (commands, transformers, mutators) שאילתות מבצעות שינוי במצב המופשט כגון: משיכה, הפקדה (queries, accessors) בנאים מחזירות ערך ללא שינוי המצב המופשט כגון: בירור יתרה (constructors) יצירת עצם חדש כגון: יצירת חשבון חדש state) (abstract של ה עצם
חתימ ה של פקו דו ת בד"כ פקודות אינן מחזירות ערך (גם לא ערך שגיאה) וחתימתן היא עם טיפוס ערך מוחזר void לפ ע מים פקודות מחזירו ת הפנייה לעצ ם הנוכחי (this) בע ד: מאפשר הרכבה של פקודות נג ד: מטשטש את הה בח נ ה בין שאילתה ופ ק ודה x.command1(); x.command2(); x.command3(); x.command1().command2().command3(); e.g. new StringBuilder().append("19").append(84).toString(); 7
שאילתות BankAccount ברור יתרה: ארגומנטים? מה טיפוס הערך המוחזר? תנאי קדם? תנאי בתר? פרטים על החשבון: מספר חשבון? פרטים על בעל החשבון? תעודת זהות? גיל? 8
שאילתות BankAccount public class BankAccount { public double getbalance() { return balance; public long getaccountnumber() { return accountnumber; public Customer getowner () { return owner; private double balance; private long accountnumber; private Customer owner; בעולם ה"אקדמי" מקובל לגשת לנתון field בעזרת המתודה field() בשפת Java השתרשה המוסכמה כי הגישה לשדה field תעשה בעזרת המתודה getfield() שמירה על מוסכמה זו הכרחית בסביבות GUI Builders ו- JavaBeans 9
setter/getter setter/getter לא כל שדה עם נראות פרטית (private) ציבורי צריך יצירה 'אוטומטית' של שרותים אלו עבור כל שדה פוגמת בעקרון הסתרת המידע ועם זאת, עדיין יש חשיבות לגישה לנתונים דרך מתודות. מדוע? למשל: נתבונן בשדה: private double balance האם דרוש?getter כן, זהו חלק מהממשק של חשבון בנק האם דרוש?setter לא בהכרח, פעולות של משיכה או הפקדה אמנם משפיעות על היתרה, אבל פעולה של שינוי יתרה במנותק מהן אינה חלק מהממשק 10
פקוד ת ה-'להפקיד' deposit המתודה: סכום הכסף המופקד מת ווסף לית רה בחשבון ארגומנטי ם? ערך מוח ז ר? תנאי קדם? תנאי בתר? מ ו סכ מ ה: שמ ו ת פק וד ו ת הם שמ ו ת פ ועל 11
פקוד ת ה-'להפקיד' /** * Makes a deposit to the current account * @pre amount > 0, "amount is positive" * @post getbalance() == $prev(getbalance()) + amount, * "balance updated according to deposit" */ public void deposit(double amount) { balance += amount; 12
ה מת ודה: withdraw פקוד ת ה-'למשוך' סכום הכסף המבוקש יורד מיתרת החשבון. אין באפשרותו של הלקוח להיכנס למצב של משיכת יתר ארגומנטים? ער ך מוחזר? תנאי קד ם? תנאי בתר? תנאיי קדם לא יבדקו בגוף המתודה והוא שגוי בכמה היבטים זה ו תכנות מתגונן 13
פקוד ת ה-'למשוך' /** * Withdraw amount from the current account * @pre amount <= getbalance(), "can't overdraft" * @pre 0 < amount, "amount is positive" * @post getbalance() == $prev(getbalance()) - amount, * "balance updated according to withdraw" */ public void withdraw(double amount) { balance -= amount; 14
דיון העברה בנ קאית נדון במספר חלופות למימוש העברת סכום מחשבון לחשבון אפשרות א': העמסת withdraw ו- deposit שיקבלו 2 ארגומנטים: סכום והפנייה לחשבון נוסף. לדוגמא: /** * Makes a transfer of amount from other to the current account * @pre 0 < amount, "amount is positive" * @pre amount <= other.getbalance(), "other can't overdraft" * @post getbalance() == $prev(getbalance()) + amount, * balance updated * @post other.getbalance() == $prev(other.getbalance()) amount, * balance of other updated */ public void deposit(double amount, BankAccount other) { other.withdraw(amount); balance += amount; 15
דיון העברה בנ קאית ניתן לתת למתודות שמות מפורשים יותר, כגון transferto או :transferfrom /** * Makes a transfer of amount from current to * the other account... */ public void transferto(double amount, BankAccount other) { other.deposit(amount); balance -= amount; 16
דיון העברה בנ קאית אפשרות ב' מתודה סטטית (הסבר בהמשך הקורס) שתקבל שני חשבונות בנק ותבצע ביניהם העברה: /** * Makes a transfer of amount from one account to the other * @pre 0 < amount <= from.getbalance(), "from can't overdraft" * @post to.getbalance() == $prev(to.getbalance()) + amount * @post from.getbalance() == $prev(from.getbalance()) - amount */ public static void transfer(double amount, BankAccount from, BankAccount to) { from.withdraw(amount); to.deposit(amount); 17
שמו רת המח לקה invariant) (class צריכה להת קיים "ת מיד" לפני ואחרי ביצוע כל מתודה ציבורית אחרי הבנאי במחלקה חשבון בנק: ח שבון חייב לה יות עם יתרה אי ש לי לית לכ ל ח שבון קיים מ ספר מ ז ה ה במ ער כת לכ ל ח שבון יש בעלי ם 18
שמו רת BankAccount /** * This class represents a bank account * @inv getbalance() >= 0, * "can't overdraft" * @inv getaccountnumber() > 0, * "an account must have an identifier" * @inv getowner()!= null, * "an account must have owner" */ public class BankAccount {... 19
בנ אי תפקיד הבנאי הוא לי צור עצם חדש ו ל הביא אותו ל מצ ב ה מק יים את ש מו ר ת המח ל קה בנאי לא אמור לכלול לוגיקה נוספת פרט לכך במחלקה :BankAccount בנאי ברירת המחדל יוצר עצם שאינו מקיים את השמורה! י ש דברים שאינם באחריות המח ל קה. מי דואג שמספרי הח שבון יהיו תקיני ם? מי מנהל את מאגר הל קוחות? ה כמסה (encapsulation) ל מ ש ל: (למשל שונים זה מזה) 20
בנ אי BankAccount /** * Constructs a new account and sets its owner and * identifier * @pre id > 0, "account number must be positive" * @pre customer!= null, * "an account must have an owner" * @post getowner() == customer, * "argument was assigned * @post getaccountnumber() == id, * "argument was assigned" */ public BankAccount(Customer customer, long id) { accountnumber = id; owner = customer; 21
final מכיו ו ן שח שבון מ זו הה חד-חד ערכית עם עצם ש ל :final נהפוך ש דה זה ל- accountnumber final private long accountnumber; את השדה final) (blank יש לאתחל פעם אחת בדיוק, בתוך הבנאי של המחלקה, כפי שאנו אכן עושים כעת, מרגע שנ וצר עצם, שפת התכנות אוכפת את הצי מ וד בין העצם וה מ ז הה ש ל ו 22
חוז ה מי מו ש תנאי הקדם מיועדים ללקוח ולכן אסור להם להכיל רכיבים שאינם זמינים לו (כגון מתודות או שדות (private תנאי הבתר ושמורת המחלקה עשויים להכיל טענות "לצורכי פנים" שיסומנו ב: @imp_inv, @imp_post לדוגמא : /** * @imp_post $ret == balance, * "consistency of representation" */ public double getbalance() {... balance הוא שדה private ולכן אינו מיועד ללקוחות 23
שמו רת מ י מו ש של BankAccount /** * This class represents a bank account * @imp_inv getbalance() == balance, * balance interface is consistent with representation" * @imp_inv getowner() == owner * owner interface is consistent with representation" */ public class BankAccount {... 24
י צ י רת ת י ע ו ד או ט ו מ ט י עבודה עם javadoc והוספ ת תגיות חוז ה 25
javadoc כדי לחולל תיעוד אוטומטי עבור הקוד שכתבנו נבחר בסרגל הכלים: Javadoc Project ->Generate 26
27
ה( javadoc נזין (בעזרת הכפתור configure או בשורת המלל) את מיקומה של התוכנית.javadoc תוכנית זו כלולה בחבילת ה (Java SDK) JDK שהורדנו מאתר חברת Sun מיקום טיפוסי של החבילה הוא ב: C:\Program Files\Java\jdk1.5.x_xx\bin\javadoc.exe - xים מ ס מ נים את מ ספ ר הגרסה) 28
javadoc סמנו את הקבצים שברצונכם לתעד ב- בחרו את רמת הגישה אשר ממנה אתם מעוניינים לתעד. לדוגמא: :public רק מתודות ושדות ציבוריים יופיעו בתיעוד. תיעוד זה מיועד ללקוחות של המחלקה. נקרא גם API :private כל המתודות והשדות יכללו בתיעוד. תיעוד זה מיועד למפתחי המחלקה לצורך הגשת התרגילים בקורס זה יש לבחור ב private האפשרות Use Standard Doclet תיצור תיעוד אוטומטי בפורמט HTML במיקום שיצויין בשורת ה Destination 29
javadoc לחיצה על Finish תיצור את התיעוד המבוקש תיקייה הכוללת את דפי התיעוד תופיע בסייר החבילות בסביבת העבודה לחיצה על דפי ה html תפתח אותם בסביבת העבודה 30
הכללת החוזה ב תיעוד מחולל התיעוד התקני אינו 'מכיר' את התגיות: @pre, @post, @inv, @imp_inv, @imp_post ניתן להוסיף תגיות אלו ע"י רישום שלהן בתוכנה javadoc באופן הבא: הורידו את הקובץ taglets.jar מאתר הקורס ומקמו אותו בתיקייה לבחירתכם למשל: D:\ohad\soft1\taglets.jar בתפריט Javadoc Generate לחצו פעמיים על Next 31
32
הכללת החוזה ב תיעוד הזי נו בחל ון Extra Javadoc options את הפרטים הבאים: -taglet il.ac.tau.cs.software1.util.pretaglet -taglet il.ac.tau.cs.software1.util.posttaglet -taglet il.ac.tau.cs.software1.util.invtaglet -taglet il.ac.tau.cs.software1.util.impposttaglet -taglet il.ac.tau.cs.software1.util.impinvtaglet -tagletpath D:\ohad\soft1\taglets.jar עדכנו את מיקום הקובץ ה במחשב שלכם לסיום ליחצו על Finish taglets.jar לפי מיקומו 33
Putting it all together 34
Class Diagram BankAccount public BankAccount(Customer customer, long id) public void withdraw(double amount) public void deposit(double amount) public void transferto(double amount, BankAccount otheraccount) public double getbalance() public Customer getowner() public double getnumber() Customer public Customer(String name, String id) public String getname() public String getid() has-a 1-1 תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב Bank main 35
The Customer Class public class Customer { public Customer(String name, String id) { this.name = name; this.id = id; public String getname() { return name; public String getid() { return id; private String name; private String id; תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 36
Toy Bank Program public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer2, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 37
Object Diagram Customer: String name: String id: String: "Avi Cohen" Bank String: "025285244" main ( ) { customer1: תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 38
Toy Bank Program public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer2, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 39
Object Diagram Customer: String name: String id: String: "Avi Cohen" Bank String: "025285244" main ( ) { customer1: customer2: Customer: String name: String id: String: "Rita Stein" תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב String: "024847638" 40
Toy Bank Program public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer2, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 41
Object Diagram BankAccount: owner: balance: 0 number: 1234 Customer: String name: String id: String: "Avi Cohen" BankAccount: owner: balance: 0 number: 5678 Bank main ( ) { customer1: customer2: account1: account2: תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב Customer: String name: String id: String: "025285244" String: "024847638" String: "Rita Stein" 42
Toy Bank Program public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer1, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 43
Object Diagram BankAccount: owner: balance: 0 number: 1234 Customer: String name: String id: String: "Avi Cohen" BankAccount: owner: balance: 0 number: 2984 BankAccount: owner: balance: 0 number: 5678 Bank main ( ) { customer1: customer2: account1: account2: account3: תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב Customer: String name: String id: String: "025285244" String: "024847638" String: "Rita Stein" 44
Message Sequence Chart public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer2, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 45
Message Sequence Chart time main account1 account2 account3 owner: balance: 0 number: 1234 owner: balance: 0 number: 5678 owner: balance: 0 number: 2984 1000 deposit מיהו this במהלך ביצוע?deposit תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 46
Message Sequence Chart time main account1 account2 account3 owner: balance: 1000 number: 1234 owner: balance: 0 number: 5678 owner: balance: 0 number: 2984 1000 deposit void תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 47
Message Sequence Chart public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer2, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 48
Message Sequence Chart time main account1 account2 account3 owner: balance: 1000 number: 1234 owner: balance: 500 number: 5678 owner: balance: 0 number: 2984 1000 deposit void 500 void deposit מיהו this במהלך ביצוע?deposit תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 49
Message Sequence Chart public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer2, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 50
Message Sequence Chart time main account1 account2 account3 owner: balance: 1000 number: 1234 owner: balance: 500 number: 5678 owner: balance: 0 number: 2984 1000 deposit void 100, account3 500 void transferto deposit מיהו ביצוע תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב this במהלך?transferTo 51
Message Sequence Chart time main account1 account2 account3 owner: balance: 900 number: 1234 owner: balance: 500 number: 5678 owner: balance: 100 number: 2984 1000 deposit void 500 deposit 100, account3 void transferto 100 deposit void void מיהו this במהלך ביצוע?deposit תכנות מתקדם בשפת Java אוניברסיטת תל אביב 52
Message Sequence Chart public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer2, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 53
Message Sequence Chart time main account1 account2 account3 owner: balance: 900 number: 1234 owner: balance: 200 number: 5678 owner: balance: 100 number: 2984 1000 deposit void 500 deposit 100, account3 void transferto 100 deposit void void 300 withdraw תכנות מתקדם בשפת Java אוניברסיטת תל אביב void 54
Output public class Bank { public static void main(string[] args) { Customer customer1 = new Customer("Avi Cohen", "025285244"); Customer customer2 = new Customer("Rita Stein", "024847638"); BankAccount account1 = new BankAccount(customer1, 1234); BankAccount account2 = new BankAccount(customer2, 5678); BankAccount account3 = new BankAccount(customer2, 2984); account1.deposit(1000); account2.deposit(500); account1.transferto(100, account3); account2.withdraw(300); output: account1 has 900.0 account2 has 200.0 System.out.println("account1 has " + account1.getbalance()); System.out.println("account2 has " + account2.getbalance()); תכנ ות מתקד ם בשפת Java א ונ י ב ר ס יטת תל אב י ב 55