דף הסברים ריק
חלק 9 בפני עצמה המחלקה כּ ישות
דף הסברים ריק
חיסכון ביצירת עצמים יותר; שאינם נחוצים Version ננסה למחזר עצמים מסוג נחזיר אותו למאגר של מסוים, יותר עצם כאשר לא צריך עצמים חופשיים עצם קיים במאגר אם יש מהמחלקה, כאשר צריך עצם חדש אחרת ניצור עצם חדש בו, נשתמש בשימוש, שאינו זה כמובן דורש שלקוחות שצריכים עצם כזה ימנעו מקריאה לבנאי, כי זה תמיד ייצור עצם חדש class Version { private static Version free_list; private Version() {} public static Version alloc() { } public Version free() { }
דף הסברים ריק
חיסכון ביצירת עצמים (המשך) class Version { private static Version free_list; private Version () {} public static Version alloc() { } public void free() { } } String value; Version previous; לקוחות לא יכולים לבנות עצמים ישירות הפרוצדורה alloc כן יכולה לבנות עצמים
דף הסברים ריק
public static Version alloc() { if (free_list == null) return new Version(); else { Version v = free_list; free_list = v.previous; return v; } } public void Version free() { this.value = null; this.previous = free_list; free_list = this; } הקצאה ושחרור
דף הסברים ריק
השדה free_list משותף לכל העצמים מהמחלקה; שדה מוכרז כשדה מחלקה (להבדיל משדה מופע) בעזרת מילת המפתח static בשירות של המחלקה, השם previous קשור לשדה של העצם שהפעיל את השירות (שדה מופע) אבל השם free_list קשור לשדה משותף לכל העצמים מהמחלקה (שדה מחלקה, (class field ;(class method) היא פרוצדורה או שירות מחלקה alloc היא מופעלת ישירות על ידי Version.alloc() ולא על עצם שהוא מופע של המחלקה; יש לה גישה לשדות המחלקה, והיא יכולה להשתמש במחלקה כלקוח, אבל כלקוח יש לה גישה גם לשדות ושירותים מוגנים private) וכדומה) שדות מחלקה ושירותי מחלקה
דף הסברים ריק
כי הם מהווים שמות גלובליים (כלומר התייחסות שתמיד אפשר למצוא, לעומת עצמים רגילים שצריך לקבל מאיזשהו עצם אחר התייחסות אליהם) כי הם יחידים (כלומר יש בדיוק Version.free_list אחד בתוכנית) זה בדיוק מה שרצינו: להיות מסוגלים למצוא את מאגר העצמים הפנויים, גם איננו מכירים אף עצם מהמחלקה, ושיהיה רק מאגר פנויים אחד למה שדות מחלקה?
דף הסברים ריק
בג'אווה הזיכרון מנוהל אוטומטית; כל עוד יש התייחסות לעצם, הזיכרון שלו לא יוקצה למטרה אחרת, ואם אין התייחסות לעצם, הזיכרון שלו יוחזר למערכת (אולי לא מייד) למרות זאת, הדוגמה מראה שאפשר להגדיר מחלקות שמממשות ניהול זיכרון מפורש, ולכן גם אפשר ליצור תוכניות עם פגמים בניהול הזיכרון, כמו בשפות ללא ניהול אוטומטי Version v1 = Version.alloc(); we use the object that v1 refers to v1.free(); Version v2 = Version.alloc(); v1.value = "something"; an error! בדרך כלל הפגם יותר קשה לגילוי מאשר בדוגמה הערות לגבי הקצאת זיכרון
דף הסברים ריק
דליפת זיכרון leak) (memory הוא פגם אחר בניהול זיכרון מפורש: עצם מוקצה אבל לא משוחרר מפורשות (האם דליפת זיכרון היא פגם בדוגמה שלנו?) java.io.fileinputstream is = new FileInputStream("C:\grades.dat"); we the input stream is.close(); we release the object; shouldn't use it later מערכת ההפעלה מאפשרת לפעמים לפתוח רק מספר מוגבל של קבצים, ולכן קובץ פתוח הוא משאב מוגבל; חשוב לשחרר את המשאב ברגע שלא צריכים אותו אי שחרור בזמן הוא פגם בתוכנית, ושימוש בקובץ ששוחרר גם הוא פגם הקצאת זיכרון (ומשאבים אחרים)
דף הסברים ריק
עוד שימושים לשדות מחלקה יש עוד תבניות תיכון בשדות מחלקה patterns) (design שמשתמשים תבנית היחיד מבטיחה שיש רק עצם אחד ממחלקה מסוימת (singleton pattern) תבנית משקל נוצה pattern) (flyweight יוצרת מאגר של עצמים מקובעים ודואגת שבכל רגע נתון יהיה לכל היותר עצם אחד עם מצב מופשט נתון; זה חוסך זיכרון ומאפשר לבצע השוואות עם == במקום equals השירות java.lang.string.intern() מתחזק תבנית כזו: הוא מחזיר התייחסות לעותק קנוני של מחרוזת
דף הסברים ריק
קבועים שדות מחלקה משמשים גם לציון קבועים בתוכנית בדרך כלל שמות שכוללים רק אותיות סגנונית: מוסכמה גדולות class Paragraph { public final static int DIR_LTR =0x01; public final static int DIR_RTL =0x02; public final static int ALIGN_LEFT =0x04; public final static int ALIGN_RIGHT=0x08; public Paragraph (int style) { } }
דף הסברים ריק
שימוש בקבועים class Paragraph { public final static int DIR_LTR =0x01; public Paragraph (int style) { } } Paragraph p = new Paragraph( Paragraph.DIR_RTL Paragraph.ALIGN_RIGHT );
דף הסברים ריק
המשתמר של שדות המחלקה (נניח את ההנחה הסבירה ששדות המחלקה מוגנים מגישה בחוזה) מופיעים ישירה על ידי לקוחות של המחלקה ולכן אינם המחלקה? מי משתמש בשדות שירותי מחלקה ושירותי מופע השירותים שלו הם עצמאי; שדות המחלקה מהווים מעין עצם אבל לכל עצם מהמחלקה יש התייחסות המחלקה; שירותי אליו ומותר לשירותי המופע לגשת אליו לשדות המחלקה יהיה משתמר משלהם לכן, כי אותו, שירותי המופע חייבים לכבד גם שירותי המחלקה וגם לשדות המחלקה לכולם יש גישה
דף הסברים ריק
class Paragraph { public final static int DIR_LTR =0x01; אבל לפעמים זה לא מספיק; לפעמים המשתמר מסובך מדי, ולפעמים האתחול עלול להודיע על חריגים שצריך לטפל בהם לפעמים המשתמר של שדות המחלקה פשוט ואתחול האוטומטי או הידני שלהם מבטיח את קיומו, class Version { private static Version free_list; null is ok אתחול שדות המחלקה
דף הסברים ריק
אתחול סטאטי class Sentence { private static Set prepositions; static { prepositions = new HashSet(); try { FileReader r = new FileReader("preps.txt"); read the file and fill the set } catch (IOException e) { } }
דף הסברים ריק
מהמחלקה, האתחול של הבסיס יתבצע לפני האתחול של המרחיבה האתחול הסטאטי יתבצע לפני הפעלת שירות כולל שירותי מחלקה וכולל בנאים (ואולי גם פסוקי אתחול אם יש כמה גושי אתחול סטאטיים עדיף לרכז סגנונית, הופעתם; הם יתבצעו לפי סדר פשוטים) את כל האתחולים הסטאטיים לגוש אחד במחלקה מרחיבה שגם למחלקת הבסיס שלה יש אתחול סטאטי, אחרות, מפעיל שירותים של מחלקות אם האתחול הסטאטי הן יאותחלו קודם סביבת זמן הריצה בוחרת בעצמה את זמן האתחול הסטאטי בהתחשב באילוצים הללו חוקי האתחול
דף הסברים ריק
במקום (singleton) עדיף אולי להשתמש בתבנית היחיד עצם מצב המחלקה נהפך למצב של מרובים; בשדות מחלקה שמשותף לכל העצמים במחלקה רגיל, class MyClassStatic { instance fields public MyClassStatic () { } instance methods } class MyClass { private final static MyClassStatic mcs = new MyClassStatic(); singleton במקום אתחול סטאטי מסובך
דף הסברים ריק
השתקפות (reflection) בג'אווה, המבנה של הקוד (מחלקות, שירותים, בזמן ריצה ומאפשר לחקור את מבנה הקוד ושדות) יש מחלקה שהעצמים שלה מייצגים מחלקות, מחלקה שמייצגת חבילות, מחלקה לשירותים, מחלקה לשדות, ומחלקה לבנאים זמין ניתן להפעיל בנאים ושירותים בעזרת העצמים המייצגים java.lang.class java.lang.package java.lang.reflect.constructor java.lang.reflect.method java.lang.reflect.field
דף הסברים ריק
המחלקה VersionedString vs = Class x = vs.getclass(); an Object method Class y = VersionedString.class; literal Class z = Class.forName("VersionedString"); static lookup העצם שמייצג את המחלקה יכול להחזיר את כל הפרטים לגביה: את שמה, את מי היא מרחיבה ומממשת, את השדות שלה, את השירותים והבנאים שלה למשל, בניית עצם תוך שימוש בבנאי ברירת המחדל: VersionedString a = (VersionedString) z.newinstance();
דף הסברים ריק
המחלקה java.lang.reflect.proxy מאפשרת לבנות מחלקות באופן דינאמי; אבל בדרך כלל יש דרך יותר פשוטה להשיג את אותה מטרה class VSHandler implements InvocationHandler { public Object invoke(object proxy, Method m, Object[] args) { if (m.getname().equals("add")) { } else if (m.getname().equals("length")) בניית מחלקות באופן דינאמי
דף הסברים ריק
בנייה דינאמית של עצמים כאלה שימושית כאשר מתקיימים שני תנאים ל מה זה טוב? ראשית, כאשר העצם הדינאמי הוא נציג (proxy) של עצם רגיל שאנו מבקשים להוסיף לו יכולת מסויימת ה- InvocationHandler כאשר קבוצת השירותים של העצם הרגיל לא ידועה מראש את העצם הרגיל נעביר לבנאי של יפעיל את השירות המבוקש על העצם invoke והשירות הרגיל שנית, מגלה אותה ב זמן ה- InvocationHandler אלא, ריצה תוך שימוש בהשתקפות זה מאפשר, למשל, להוסיף שכבה של בדיקת הרשאות מול הגדרות בקובץ עבור אוסף שרירותי של מחלקות
דף הסברים ריק
סיכום שדות מחלקה והשתקפות שדות מחלקה משותפים לכל העצמים במחלקה לשדות המחלקה יש שמות גלובליים ידועים והם יחידים על כל השירותים של משלהם; יש משתמר לשדות המחלקה לכבד את המשתמר מחלקה, שירותי מופע ושירותי המחלקה, ולא ושירותים, המחלקה היא לא רק אוסף הגדרות של שדות רק מבנה הנתונים של שדות המחלקה עצם שניתן לחקור ריצה, המחלקה היא גם עצם מוחשי בזמן השדות והשירותים אותו ולקבל ממנו עצמים שמייצגים את אפשר להפעיל בנאים ושירותים בצורה כזו אפשר ליצור עצמים באופן דינאמי בעזרת java.lang.reflect.proxy
דף הסברים ריק
חלק 10 שׁ יוּם (Naming)
דף הסברים ריק
מרחב השמות בתוכנית ג'אווה, כפי שהצגנו אותו עד כה, הוא מרחב דו שכבתי, כמעט שלם, עם חוקי נראות (visibility) דו מימדיים שלמות: לכל דבר יש שם; ראינו שניתן להעביר לשירות עצם אבל לכל טיפוס היה עד כה שם או מערך אנונימי, vi.add( new Integer (3) ); printprimes( new int[] { 1, 2, 3, 5, 7 } ); שם מחלקות; שבכל אחת יש חבילות, אוסף של דו שכבתיות: אוסף החבילות המחלקה; טיפוס מורכב משם החבילה ושם שטוח שטוח (למרות שהוא נראה היררכי) ואוסף המחלקות בחבילה מרחב השמות בתוכנית ג'אווה דו מימדיות: נראות מוחלטת, בחבילה, או ליורשים
דף הסברים ריק
בעצם מרחב השמות יותר מורכב יש לו יותר משתי שכבות: אפשר להגדיר מחלקות בתוך מחלקות, ואפילו מחלקות בתוך שירותים ואפשר גם ליצור מחלקות אנונימיות בחלק הזה של הקורס נראה את המנגנונים הללו וגם נגדיר בצורה מדוייקת את חוקי הנראות
דף הסברים ריק
מחלקות פנימיות סטאטיות הסוג הפשוט ביותר של מחלקה פנימית public class PersistentVersionedString implements VersionedString { public static class PVSFilter implements java.io.filefilter { public boolean accept(java.io.file f) { return f.getname().endswith(".pvs"); } } }
דף הסברים ריק
דבר, היא מחלקה רגילה לכל מחלקה כזו FileFilter filter = new PersistentVersionedString.PVSFilter(); File dir = new File("/Projects/oopj"); File[] files = dir.listfiles( filter ); אין שום לחלוטין; עצמים מהמחלקה הפנימית הם עצמאיים קשר בינם ובין עצמים מהמחלקה החיצונית שירותים החיצונית, שהפנימית היא מעין שדה של אבל בגלל גם שתיהן, של שתיהן יכולים לגשת לכל שדות המחלקה של ו- protected ) private) לשדות מוגנים שימוש במחלקה פנימית סטאטית
דף הסברים ריק
אם המחלקה הפנימית אינה ציבורית (אינה מוגדרת,(public הטיפוס שלה מוסתר, אבל עצמים מהמחלקה אינם מוסתרים אם יש התייחסות אליהם public class PersVS { private static class PVSFilter... { } public static FileFilter getfilter() { return new PVSFilter(); } הגנה על מחלקות פנימיות סטאטיות FileFilter f = new PersVS.PVSFilter(); error FileFilter f = PersVS.getFilter(); ok
דף הסברים ריק
עוד שימוש למחלקה פנימית Version הוא מחלקת עזר במימוש של,LinkedVersionedString עדיף להסתיר אותה public class LinkedVersionedString extends VersionedString { private class Version { } }
דף הסברים ריק
מבנה מיותר בג'אווה; מעט מאוד שימושים אמיתיים ובכל זאת, מה זה? מחלקה של עצמים שכל אחד מהם "שייך" לעצם של המחלקה המכילה ומכיר את שדות המופע שלו public class Outer { private int o; public class Inner { private int i; public void set() { i = o; } public int get() { return i; } } מחלקות פנימיות לא סטאטיות
דף הסברים ריק
קשירה של מחלקה פנימית public class Outer { private int o; public class Inner { } public Inner getinner() { return new Inner(); } public void increment() { o++; } } Outer x = new Outer(); Outer.Inner y1 = x.getinner(); Outer.Inner y2 = x.getinner();
דף הסברים ריק
קשירה של מחלקה פנימית (המשך) Outer x = new Outer(); Outer.Inner y1 = x.getinner(); Outer.Inner y2 = x.getinner(); x.increment(); now x.o == 1 y1.set(); y1.i = x.o == 1 x.increment(); now x.o == 2 y2.set(); y2.i = x.o == 2 y1.get(); returns 1 y2.get(); returns 2
דף הסברים ריק
אותה תוצאה עם מחלקה פנימית סטאטית public class Outer { public static class SInner { private int i; private Outer outer; an explicit reference public SInner(Outer outer) { this.outer = outer; } public void set() { i = outer.o; } public SInner getinner() { return new SInner(this); }
דף הסברים ריק
תחביר (מסובך) לשימוש במח' פנימיות בנייה ישירה של עצם פנימי Outer x = new Outer(); Outer.Inner y3 = x.new Outer.Inner(); הרחבה של מחלקה פנימית על ידי מחלקה רגילה class SubInner extends Outer.Inner { public SubInner (Outer outer) { outer.super(); } invoke the super's constructor שימוש בשדה מוסתר של המחלקה החיצונית על ידי הפנימית, ובשדה מוסתר של המחלקה שהחיצונית מרחיבה Outer.this.field Outer.super.field
דף הסברים ריק
מחלקה מקומית (לא שימושי) public VersionedString somemethod() { final int fi = 3; int mi = 4; class LocalVS implements VersionedString { public void add(string s) { int x = fi; ok int y = mi; compilation error; mi is not final } } return new LocalVS(); }
דף הסברים ריק
תכונות מחלקה מקומית זו מחלקה פנימית לא סטאטית; אסור להשתמש במילת המפתח static הטיפוס לא מוכר מחוץ לגוש (שירות) שבו היא מוגדרת, אין צורך בהגדרת נראות private) או (protected מותר לה להשתמש במשתנים של הגוש (שירות) שבו היא מוגדרת; זו הסיבה להגדירה מקומית ולא סתם פנימית ולכן העצמים עצמם יכולים לחמוק מהגוש שבו המחלקה מוגדרת, ולכן מותר לה לגשת רק למשתנים שערכם לא ישתנה אחרי סיום פעולת הגוש (משתנים שמוגדרים,(final אחרת היא הייתה עלולה לגשת למשתנים שכבר לא קיימים אין למחלקות כאלה הרבה שימושים
דף הסברים ריק
מחלקות אנונימיות (מאוד שימושיות) מחלקות אנונימיות הן בעצם הסיבה להגדרה של מחלקות פנימיות ומקומיות משמשות בדרך כלל לאריזה של פרוצדורה שמיועדת להישמר במבנה נתונים להפעלה בעתיד Button b = new Button( ); b.addmouselistener( new MouseListener() { public void mouseclicked(event e) { } }); העברנו לכפתור פרוצדורה שהוא אמור להפעיל כאשר לוחצים על הכפתור; הפרוצדורה הזו תגרום לתוצא הלוואי הרצוי; כדי לגרום לתוצא הלוואי, היא צריכה לשמור התייחסות לעצמים שהיא תשנה את מצבם; פרוצדורה כזו נקראת closure
דף הסברים ריק
שימוש טיפוסי במחלקה אנונימית interface MouseListener { public void mouseclicked(); } class Button { Set mouse_listners = new TreeSet(); public void addmouselistener( MouseListener ml) { mouse_listeners.add(ml); } } b.addmouselistener( new MouseListener() { public void mouseclicked(event e) { });
דף הסברים ריק
סיכום ביניים: מחלקות פנימיות הסוג השימושי ביותר הוא מחלקות אנונימיות, משום שהוא מפצה על היעדר התייחסויות לפרוצדורות בג 'אווה מחלקות אנונימיות מאפשרות להעביר לעצם פרוצדורה כזו, שהתנהגותה תלויה בהקשר שבו הוגדרה (כי היא יכולה להשתמש בשדות של העצם ובמשתנים מקובעים של השירות שבו היא מוגדרת) מחלקות אנונימיות שימושיות בתבניות התיכון,observer ו- command,strategy מחלקות פנימיות סטאטיות מאפשרות לעצב את מרחב השמות באופן גמיש ולהסתיר מחלקות עזר השאר (פנימיות לא סטאטיות ומקומיות) פחות שימושיות עדיף להגביל את התלות בין המחלקה החיצונית והפנימית
דף הסברים ריק
הגנה על שמות ארבע רמות הגנה על שמות:,protected,public,private ו- package (בלי מילת מפתח) רק השמות מוגנים; העצמים עצמם לא ההגנה היא ביחס למבנה הסטאטי של הקוד, לא ביחס למבנה הדינאמי של עצמים בזיכרון: האם שורת קוד נתונה מסוגלת להתייחס לשם מסוים מה מוגן? מחלקות (כולל פנימיות) שדות, שירותים מה לא מוגן? משתנים ומחלקות בתוך שירותים/גושי פסוקים, טיפוסים אנונימיים (אין שם שאפשר להגן עליו)
דף הסברים ריק
אין הגבלות :public כולל כל מחלקה, שימוש רק על ידי קוד באותה :private (כולל המחלקות פנימיות שמוגדרות באותה מחלקה ראשית אחרות) מחלקות פנימיות גישה למחלקות מתיר אבל private כמו :protected מרחיבות אבל מתיר גישה לכל שירות private כמו חבילה: הגנת באותה חבילה "סגירת" אפשר להגביל את התחום של הגנת חבילה על ידי זה מתבצע מחלקות; חבילה כך שאחרים לא יוכלו להוסיף לה ה- jar בקובץ (sealed) על ידי סימון החבילה כחתומה רמות הגנה
דף הסברים ריק
מחלקות שוכנות בחבילות כל קובץ מזהה את החבילה שלה הוא שייך, ומיקום הקובץ צריך להתאים להיררכיה של שם החבילה package il.ac.tau.oopj; למרות ששמות החבילות נראים היררכיים, ולמרות שקבצי קוד המקור והקבצים הבינריים.java) ו- class.) מאורגנים במדריכים באופן היררכי שמשקף את שמות החבילות, מבחינת הגנה על שמות אין היררכיה בין חבילות כלומר למחלקה בחבילה il.ac.tau.oopj.ex3 אין גישה לשמות עם הגנת חבילה ב- il.ac.tau.oopj ולא להיפך חבילות ושמות חבילות
דף הסברים ריק
השם המלא של חבילה כולל את שם החבילה ושם המחלקה, למשל, java.io.inputstream אבל הוא משמעיות, שימוש בשמות כאלה מקנה לקוד חד בייחוד כאשר שמות החבילות ארוכים ולאחר שם מסורבל, כמו למשל שדה, או שם שירות, המחלקה בא שם java.lang.system.println אפשר לייבא שמות מחלקות ספיציפיות או את כל שמות קוד: המחלקות מחבילה לקובץ import java.io.inputstream; import java.lang.*; ומעלה אפשר גם לייבא את שמות הקבועים 1.5 בג'אווה (static import) במחלקה שמוגדרים שמות מלאים ויבוא שמות
דף הסברים ריק
מחלקות פנימיות מאפשרות להגדיר מרחב שמות היררכי שמשקף את מבנה הקוד (מי משתמש במי, מה שייך למה) מחלקות אנונימיות שימושיות בעיקר על מנת לייצג פרוצדורות קשורות חלקית (closures) במבני נתונים כדאי להימנע מתלויות סבוכות בין מחלקות חיצוניות ופנימיות ומתחביר לא טריוויאלי בחירת רמת ההגנה לשם דורשת בחירה בין יכולת שימוש והרחבה ובין מודולריות protected היא רמת ההגנה הבעייתית ביותר, כי אין לנו מושג מי ירחיב ומתי מחלקות פנימיות מאפשרות תיחום מודולריות יותר מדוייק סיכום מרחב השמות
דף הסברים ריק
חלק 11 בדיקות (Testing)
דף הסברים ריק
איך יודעים שמודול או תוכנית נכונים? פורמאלי פורמאלי או לא שמיועד לוודא באופן תהליך אימות: נכונות של מודול או תוכנית ביחס לחוזה (לא אימות פורמאלי אוטומאטי אינו אפשרי במקרה הכללי כריע) פורמאלי ידני יקר מדי לרוב המערכות פרט אולי אימות מוטסות, (רפואיות, למערכות שחיי אדם תלויים בהן ישירות ראוי) אבל גם שם יש פחות אימות ממה שהיה וכולי, ביצוע סדרת הרצות של התוכנה על מנת :(testing) בדיקות ולהגדיל את בטחוננו יש, אם פגמים, שמיועדות למצוא בנכונותה ומועיל מאוד באופן מכלום, אבל יותר טוב נכונות, לא מבטיח מעשי להקטנת מספר הפגמים
דף הסברים ריק
כאשר המכונית לא עוברת טסט, זה כמובן מעצבן, אבל זה בדרך כלל לא כישלון של מכון הרישוי שביצע את הטסט כישלון והצלחה של בדיקה הם נפרדים לחלוטין מאלה של כי הוא מספק אפשרות לתיקון פגם לפני שהוא גורם עוד נזק הנבדק! הקוד פגם היא מגלה אם מצליחה בדיקה מגלה פגם או מדווחת על פגם לא נכשלת אם היא לא בדיקה קיים הבדיקה, לא עבר את בדיקה מדווחת על פגם נאמר שהקוד אם ולא נאמר שהבדיקה נכשלה חיובי) אבל אולי, (לא משמח דווח על פגם הוא אירוע חיובי מינוח שמשקף גישה בריאה לחיים
דף הסברים ריק
בדיקות יחידה tests) (unit בודקות מודול בודד מחלקה אחת או מספר מחלקות קשורות) (בדרך כלל בדיקות אינטגרציה בודקות את התוכנית כולה, או קבוצה של מודולים ביחד; מתבצעת תמיד לאחר בדיקות היחידה של המודולים הבודדים (כלומר על מודולים שעברו את בדיקות היחידה שלהם) בדיקות קבלה tests) (acceptance מתבצעות על ידי הלקוח או על ידי צוות שמתפקד בתור לקוח, לא על ידי צוות הפיתוח גם לאחר כניסה לשימוש, התוכנה ממשיכה למעשה להיבדק, אבל אצל משתמשים אמיתיים; רצוי שיהיה מנגנון דיווח לתקלות ופגמים שמתגלים בשלב הזה, ורצוי לתקן את הפגמים הללו שלושה סוגי בדיקות
דף הסברים ריק
קופסאות שחורות וקופסאות פתוחות על כל מודול תוכנה צריך לבצע שני סוגים של בדיקות יחידה בדיקות קופסה שחורה tests) (black-box בודקים את הקוד מול החוזה שהוא מבטיח לקיים, והן אינן תלויות במימוש בדיקות כיסוי tests) coverage או (glass-box tests דואגות שבזמן הבדיקות, כל פיסת קוד תרוץ, ובמקרים מסוימים, תרוץ יותר בכמה צורות בדיקות קופסה שחורה לא תלויות במימוש ולכן אותו סט בדיקות תקף לכל המימושים של מנשק מסוים, גם העתידיים, ובפרט לשינויים ותיקונים במימוש הנוכחי בדיקות כיסוי צריך לעדכן כאשר מעדכנים את הקוד
דף הסברים ריק
את החוזה מביאים את תוכנית הבדיקה למצב שבו היא שירות, עבור כל ובודקים שתנאי לשירות, קוראים הקדם, מקיימת את תנאי האחר מתקיים אז צריך הקדם; לפעמים יש יותר מדרך אחת לקיים את תנאי לבדוק דרכים שונות ברור שלפעמים יש מספר עצום של דרכים לקיים את תנאי צריך לבדוק דרכי קיום כולן; הקדם ואי אפשר לבדוק את וצריך לבדוק מקרי קצה "או" שונות של פסוקי מה בודקים בקופסה שחורה?
דף הסברים ריק
למשל, עבור תנאי הקדם 0<=i<=length() צריך לבדוק את המקרה,i=length()=0,i=0<length(),0<i=length(),0<i<length() וגם מקרה אחד לפחות שבו 2=i מקרי הקצה (0=i ו-() i=length בדוגמה) מסייעים למצוא מקרים שבהם שכחנו לממש טיפול במקרי קצה (למשל שכחנו לטפל באופן נפרד במקרה שבו שדה מכיל null וכו') מקרים נוספים: מחרוזות ריקות וקצרות, מערכים ריקים, שני ארגומנטים או יותר שמתייחסים לאותו עצם או מערך אם השירות יכול לקיים אחד מתוך כמה תנאי אחר (למשל, "יוחזר מספר הגרסאות או שנודיע על חריג קלט/פלט") אז הבדיקה צריכה לגרום לו לקיים כל אחד מהם קופסה שחורה (המשך)
דף הסברים ריק
אמרנו שהבדיקות בודקות את התנהגות הקוד מול החוזה אבל יתכן גם שהדרישה יתכן שהקוד פגום, נכשלת, אם בדיקה בחוזה חזקה מדי החוזה? יודעים האם לתקן את הקוד או את איך החוזה כמובן שאם עבדנו בצורה מסודרת וחשבנו על לקוחות), ואולי גם השתמשנו בו להוכחת נכונות של הסיכויים שהבעיה היא במימוש אבל ככל שמטפסים ממחלקות בודדות לתתי מערכות שלמות (מבדיקות יחידה לבדיקות ובסוף לתוכנית השלמה מכיוון שמחלקות עולה, הסיכוי שהחוזה פגום אינטגרציה), שקשה יותר ומכיוון סטנדרטיות ברמות נמוכות הן יותר לאפיין נכון את ההתנהגות הנכונה של מערכות מורכבות אבל האם החוזה "נכון"?
דף הסברים ריק
כי אי אפשר לבדוק באופן ממצה את כל הדרכים לקיים את תנאי הקדם; מספר הדרכים עצום או אינסופי ברוב המקרים public void somemethod(int depth, ) { if (depth == 23478) System.out.println("xxx"); ברור שבדיקת קופסה שחורה לא תמצא את ההתנהגות הזו נראה דמיוני אבל זה לא; תוכניתן השתמש בקטע הקוד הזה כדי לקבוע במנפה (debugger) נקודת עצירה שלא מופעלת בכל הפעלה של השירות דוגמאות אחרות: מימוש מסוים של מיון למערכים קטנים, מימוש אחר לגדולים; פסוק if בתוך השירות בוחר את המימוש למה בדיקות קופסה שחורה לא מספיקות?
דף הסברים ריק
בדיקות שמיועדות לגרום לכל פיסת קוד לרוץ בדיקות כיסוי בכל פסוק תנאי צריך לקבל את שתי התוצאות האפשריות ( then/else ) לולאות צריך לבצע אף פעם, פעם אחת, ושתי פעמים (אף פעם למקרה שהכנסנו לגוף הלולאה קוד שצריך להתבצע בכל מקרה, שתי פעמים למקרה שהמעבר בין איטרציות פגום) אם יש מספר דרכים לצאת מלולאה, צריך להשתמש בכולן כנ"ל לתנאים בוליאניים עם "או" (לבדוק את כל האפשרויות) רקורסיה דינה כדין לולאה: אף פעם, פעם אחת, שתי פעמים יש כלים אוטומטיים שמודדים כיסוי ומצביעים על קוד לא מכוסה
דף הסברים ריק
בדיקות הקופסה השחורה צריכות גם לבדוק תוצאי לוואי רצויים שמצוינים בתנאי האחר בדרך כלל בתנאי האחר יש גם פסוק סתום שאיננו מופיע מפורשות: "ופרט לכך אין לשירות תוצאי לוואי" זה לא תמיד פשוט לתחם את תוצאי הלוואי המותרים, כי ככלל אמרנו שמותר לשירות לקיים יותר ממה שהוא מבטיח (יש סגנון לכתיבת חוזים שבו משתמשים בפסוק modifies שמתאר איזה עצמים מותר לשירות לשנות; את השאר אסור לו; זהו תיחום יותר מדויק ויותר מפורש של תוצאי הלוואי) קשה לבדוק שאין לשירות תוצאי לוואי פרט למותרים; היכן לחפש? ובכל זאת, לפעמים רצוי לחשוד ולבדוק תוצאי לוואי רצויים ולא רצויים
דף הסברים ריק
היכן לחפש דליפה של תוצאי לוואי יש חשודים רגילים: עצמים ומחלקות שיש נטייה לשנות אותם בהמון מקרים, למשל מנגנונים של הקצאת זיכרון ומשאבים אחרים (קבצים פתוחים, חלונות על המסך); ראינו שגם בג'אווה אפשר לבנות מנגנוני הקצאת זיכרון בחקירה: סריקה של הקוד תראה צריך לבדוק שמצבם לא משתנה בדרכים שאסור לו להשתנות ויש חשודים ששמם עולה את מי הוא עשוי לשנות הללו, לגבי החשודים היא חלק זיכרון) (הקצאת הבדיקה של חשודים רגילים מבדיקות הקופסה השחורה הבדיקה של חשודים שנמצאו בחקירה היא כמובן חלק מבדיקות הכיסוי
דף הסברים ריק
בדיקות של היררכיית טיפוסים לגבי מחלקות שמממשות מנשק: בדיקת קופסה שחורה מול החוזה של המנשק (אם לא חיזקנו אותו) וכיסוי של המימוש לגבי מחלקות שמרחיבות מחלקות: בדיקה מלאה של מחלקת הבסיס, בדיקה של השירותים הנוספים/מחוזקים של המרחיבה, ובדיקת כיסוי של המרחיבה מחלקה מופשטת צריך לבדוק בעזרת מחלקה מוחשית מרחיבה
דף הסברים ריק
בבדיקות מעורבים שני סוגי קוד: מנועים ורכיבים חלופיים מנוע (driver) הוא קוד שמדמה לקוח של המודול הנבדק וקורא לו רכיב חלופי (stub) מחליף ספק שמשרת את המודול הנבדק למשל מחלקה A משתמשת ב- B שמשתמשת ב- C בדיקת יחידה ל- B תדמה לקוח של B ותספק מחלקה חלופית ל- C, על מנת שניתן יהיה לבדוק את B בנפרד מ- A ו- C רכיב חלופי צריך להיות פשוט ככל האפשר לפעמים הרכיב החלופי לא יכול להיות משמעותית יותר פשוט מהמודול שאותו הוא מחליף, ואז כדאי להשתמש במודול האמיתי לאחר בדיקות יסודיות שלו איך בודקים?
למה צריך לבדוק באופן יסודי מודול שמשמש בבדיקה של מודול אחר? כדי לדעת בקלות היכן לחפש את הפגם אם בדיקה מוצאת פגם. אם בודקים ביחד שני מודולים א' ו-ב', שאין לנו ביטחון בנכונות של אף אחד מהם, קשה לדעת האם פגם שנחשף בבדיקה הוא פגם במודול א' או במודול ב', וצריך לחפש בשניהם. לפעמים קשה לממש רכיב חלופי שיכול להחליף מודול אמיתי בכל מצב, אבל לא קשה לממש רכיב חלופי מוגבל מאוד שפועל לפי החוזה במקרים הספיציפים שמתעוררים בבדיקה. זה דורש תיאום בין המנוע ובין הרכיבים החלופיים של תוכנית הבדיקה. מתי הרכיב האמיתי פשוט כמו רכיב חלופי? בשני מקרים. האחד, כאשר הרכיב האמיתי פשוט מאוד, ואז קשה להמציא חלופה יותר פשוטה. והשני, כאשר הרכיב האמיתי מקיים חוזה מורכב או חוזה שקשה לקיים בדרך פשוטה. אבל התנאי השני מתקיים לעיתים רחוקות וכדאי לחשוב היטב האם אפשר בכל זאת לממש רכיב חלופי פשוט.
דורש הרבה בדיקות קופסה שחורה ; חוזה מורכב אבל מקרה, אם החוזה מבטיח ששירות יפעל בכל בפרט, (למשל את תנאי האחר במקרים שונים יקיים תנאי אחר שונים הרצוי ללקוח במקרים מסוימים והודעה על חריג במקרים אז צריך לבדוק את כל האפשרויות הללו אחרים), יותר בדיקות קופסה שחורה מחיר: לתכנות דפנסיבי יש בין אם המורכבות כיסוי, מורכב דורש הרבה בדיקות מימוש של חוזה מורכב או של שאיפה ליעילות היא תוצאה לבדוק (כפי שאמרנו קשה אם הקוד עצמו לא בודק את החוזה כדאי אולי לבדוק את תנאי הקדם ברכיבים חלופיים בג'אווה), את הביטחון שהלקוחות מקיימים את תנאי הקדם ; מגביר זה את תנאי האחר בודק המנוע דמי ביטחון
דף הסברים ריק
רצוי למצוא פגם בתוכנה קרוב ביותר לנקודה שבה נוצר הפגם עיקרון הזריזות זה נכון לגבי זמן הריצה: כדאי שהתוכנה תגלה את הפגם ותדווח עליו (למשל על ידי בדיקת החוזים והמשתמרים) קרוב ביותר לנקודה שבה הקוד הפגום פעל; זה יקל על מציאת הפגם בחיפוש אחורה מנקודת הדיווח על הפגם וזה נכון לזמן הפיתוח: כדאי שנגלה את הפגם מהר ככל האפשר לאחר שיצרנו אותו (פגמים הם תוצרי היצירתיות של מפתחים, לא תכונה מובנית של תוכנית או תוכנה בכלל); זה יקל ויוזיל את תיקונו לכן רצוי לממש בדיקות יחידה מוקדם ככל האפשר; בדיקות קופסה שחורה אפשר ורצוי לממש לפני המימוש של המודול, ובדיקות כיסוי רצוי לממש מייד לאחר המימוש; לא כדאי להתעכב
דף הסברים ריק
בכל פעם שמגלים פגם בתוכנה, בכל שלב של חיי התוכנה (גם לאחר שנכנסה לשימוש) יש להוסיף בדיקה שחושפת את הפגם, כלומר שנכשלת בגרסה עם הפגם אבל עוברת בגרסה המתוקנת לפעמים הבדיקה תתווסף לבדיקות הקופסה השחורה ולפעמים לבדיקות הכיסוי (אם הפגם קשור באופן הדוק למימוש ולא לחוזה) את סט הבדיקות השלם, כולל כל הבדיקות הללו שנוצרו בעקבות גילוי פגמים, מריצים לאחר כל שינוי במודול הרלוונטי, על מנת לוודא שהשינוי לא גרם לרגרסיה, כלומר להופעה מחודשת של פגמים ישנים סט הבדיקות מייצג, כמו התוכנה המתוקנת, ניסיון מצטבר ויש לו ערך טכני וכלכלי משמעותי בדיקות רגרסיה
דף הסברים ריק
ככלל לעולם לא כאמור, לסט הבדיקות יש ערך רב ואין טעם, בדרך כלל, להפסיק להשתמש בבדיקה שעשויה לגלות פגמים עם זאת, יש להשתמש בהגיון בריא אין טעם להשתמש בבדיקת רגרסיה שבדקה פגם שהיה קשור באופן הדוק למימוש אם החלפנו לחלוטין את המימוש אין טעם להמשיך להשתמש בסט עצום של בדיקות אם בשלב מסוים עוברים לבדיקות כיסוי מקיפות ) מ חקרים מצאו מקרים שבהם סט עצום של בדיקות לא כיסה א ת כל הקוד, שניתן היה להשתמש בחלקיק מתוכן בלי להוריד בהרבה את הכיסוי, ושניתן היה להשלים את הכיסוי במספר קטן של בדיקות נוספות; וזה לתוכנה עם סט בדיקות טוב!) מתי להפסיק להשתמש בבדיקה
דף הסברים ריק
נניח שמחלקה מסוימת משתמשת בפונקצית ספריה, בצורה נכונה לחלוטין, כלומר תוך שימוש נכון בחוזה של פונקצית הספרייה לקוח מדווח על פגם בתוכנה, ואחרי בירור מסתבר שהבעיה היא שב גרסה של הספרייה הסטנדרטית שמותקנת אצל הלקוח (נניח (JDK 1.4.1 יש פגם בפונצית הספריה ) למשל באג מספר 4302884 במחלקה java.applet. AudioClip או באגים בספריה שמשפיעים רק על גרסאות מסוימות של מערכת ההפעלה) דוגמה לבדיקה מוזרה אבל מוצדקת נוסיף בדיקה שמדמה את הפגם בספריה הסטנדרטית ונתקן את המחלקה שלנו; למי שלא היה מודע לפגם המדווח הקוד יראה כעת משונה, והבדיקה תיראה מוזרה, אבל שניהם מוצדקים והבדיקה הזו עשויה למנוע חזרת הפגם
דף הסברים ריק
כי טובה, בדיקה שדורשת התערבות של אדם היא בדיקה לא שינוי בתוכנה קשה ויקר לחזור עליה אחרי כל לכן, אוטומטית בדיקה בדידה צריכה להיות כל שמריץ את כל הבדיקות ומדווח על כל (תוכנה) וצריך מנגנון הפגמים שהתגלו למשל אם ביצענו שינוי חלק, לפעמים צריך להריץ אולי רק אבל אם הבדיקות מהירות כדאי להריץ את כולן בתוכנה; קטן בדיקות צריכות להיות אוטומטיות
דף הסברים ריק
אם התוכנה מופעלת על ידי מנשק אדם-מכונה (למשל מנשק משתמש גרפי, או על ידי דיבור, וכדומה), כדאי לבנות לה גם מנשק חלופי, לצורך בדיקות בלבד, שיפעיל ויבדוק את החלק הפונקציונאלי ובמקרים כאלה את המנשק למשתמש נבדוק לחוד יש גם כלים מיוחדים לבדיקת מנשקים גרפיים: כלים שאפשר לתכנת בהם תנועה של עכבר, הקשה על המקלדת, וכדומה לפעמים בונים חומרה מיוחדת שתפקידה להפעיל את המנשק למשתמש בזמן בדיקות, כמו זרוע רובוטית שלוחצת על reset במקרה חרום אפשר להסתמך על בודקים אנושיים שיפעילו את המערכת על פי הנחיות כתובות, אבל זה פחות אמין, זה יקר, וזה משעמם איך נמנעים מהתערבות אנושית
דף הסברים ריק
וקבלת דיווחים מהשטח קבלה, אינטגרציה, יחידה, בדיקות בדיקות קופסה שחורה לעומת בדיקות כיסוי חיפוש ממוקד של תוצאי לוואי אסורים (אפשר לממש בדיקה צריכות להתבצע מוקדם לאחר המימוש ולעיתים המודול) השחורה לפני מימוש הקופסה בדיקות את בקוד) (תכופות לעומת קצב השינויים תכופות בהמשך מיכון הבדיקות מקל על ביצוען התכוף בדיקות מצטברות ושימוש מתמשך בהן מונע רגרסיה של קוד חוזים סבוכים וקוד מורכב מובילים לעלות בדיקה גדולה סיכום נושא הבדיקות
דף הסברים ריק