2.1 אובייקטים וטיפוסי נתונים מופשטים (ADT)

מבנה נתונים ואלגוריתמים

 

 

במסגרת זו לא נתעמק בתיאוריה המלאה של תכנות מונחה עצמים. אנו נתמקד בחלק אחד של תכנות מונחה עצמים (OOD): טיפוסי נתונים מופשטים (ADT). התיאוריה המלאה מבוססת על עקרונות ה-ADT.

 

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

 

על-מנת להתאים עצמנו לתיאורית ה- OO, אנו נקרא לפונקציות ולפרוצדורות מתודות (methods), ולמבנה הנתונים ולמתודות שלו נקרא מחלקה (class). בכל אופן, למחלקות שלנו אין את מלוא היכולות אשר יש למחלקות לפי תיאוריות ה- OO. מקרה של מחלקה נקרא אובייקט. אובייקטים מייצגים אובייקטים בעולם האמיתי ומופיעים בתוכניות כמשתנים שטיפוסם מוגדר על ידי המחלקה. למונחים אלו יש את אותו מובן גם במתודולוגיה של OOD, אך היא כוללת בנוסף גם תכונות כגון ירושה שבה לא נדון במסגרת זו.

 

חשוב לשים לב כי גישת מכוונות האובייקט היא למעשה מתודולוגיית תכנון. כתוצאה מכך, ניתן לכתוב תוכניות OO על ידי שימוש בשפות שונות כגון C, Ada ופסקל. שפות "OO" כגון C++ ו- Eiffel מספקים מהדר התומך ב- OOD. תמיכה זו חייבת להיות מסופקת על ידי המתכנת בשפות שאינן OO.

 

2.1.1 דוגמא: אוסף נתונים (Collection)

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

יצירה (create) - יצירת אוסף חדש.

הוספה (add) - הוספת פריט לאוסף.

הסרה (delete) - הסרת פריט מהאוסף.

מציאה (find) - מציאת פריט באוסף, בהתאם למספר קריטריונים.

הריסה (destroy) - הריסת האוסף.

 

2.2 בנאים (Constructors) והורסים (Destructors)

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

 

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

 

2.3 מבני נתונים

כדי לבנות מודל תוכנה מופשט של אוסף נתונים, יש להתחיל בבניית הגדרות פורמליות. המרכיב הראשון הוא שמו של טיפוס הנתונים - זהו הטיפוס של האובייקטים השייכים למחלקה של אוסף הנתונים. בשפת C אנו משתמשים ב- typedef על מנת לקבוע טיפוס חדש המהווה מצביע למבנה:

 

typedef struct collection_struct *collection;

 

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

 

הצהרת ה-typedef יוצרת טיפוס (מחלקה - במונחי OO) ב- C, בשם collection , למשל. אנו יכולים להצהיר על אובייקט מסוג collection בכל עת שנצטרך. למרות ש- C חושפת כי  האובייקטים של המחלקה מוחזקים למעשה על ידי פוינטר, עדיף להתייחס לנושא מנקודת מבט מופשטת: נתייחס למשתנים מסוג collection כמייצגים אובייקטים במחלקה, ולא כמצביעים

ב- C.

 

2.4 מתודות

כעת נגדיר מהן מתודות:

collection ConsCollection(int max_items, int item_size);
void AddToCollection(collection c, void *item);
void DeleteFromCollection(collection c, void *item);
void *FindInCollection(collection c, void *key);

 

שים לב כי אנו משתמשים במספר אילתורים ("hacks") בשפת C. שפת C - אפילו בצורת ב- ANSI סטנדרטי אינה בדיוק שפת התכנות הבטוחה ביותר במובן של התמיכה שהיא מספקת לבניית תוכנה איכותית. בכל אופן, העובדה שהיא כה פופולרית מעידה כי היא בחירה פרקטית לפרוייקטים של הנדסת תוכנה. לרוע המזל, C++ בגלל שהיא מבוססת על C, אינה טובה יותר. לעומת זאת JAVA, "האופנה האחרונה" בתעשיית התוכנה, מראה כי מתכנניה למדו מניסיון העבר והגבילו מספר תכונות מסוכנות של C.

 

בדיוק כשאנו מגדירים את אובייקט אוסף הנתונים שלנו כמצביע למבנה, אנו מניחים כי האובייקטים השייכים לאוסף זה מיוצגים בעצמם על ידי מצביעים למבני הנותנים. לכן ב-AddToCollection, פריט -  item הוא טיפוס של  void *.  ב-C ANSI, void * מתאים לכל מצביע - ולכן יהיה אפשרי להשתמש ב-AddToCollection להוספת כל אובייקט לאוסף הנתונים. באופן דומה, key ב-FindInCollection הוא טיפוס של

void *, כך שמפתח אשר נעשה בו שימוש כדי למצוא פריט כלשהו באוסף, עשוי להיות בעצמו אובייקט כלשהו. FindInCollection מחזירה מצביע לפריט אשר מתאים למפתח, ולכן גם הוא מטיפוס void. השימוש ב-void *  כאן משקף את אחד משיאי הליקויים ב-C: חוסר היכולת ליצור אובייקט - על מקורי, בהשוואה ליכולת זו הקיימת ב- Ada או ב- templates

שב- C++.

 

שים לב כי יש קיצורי דרך אחרים המתגברים על מגבלותיה של שפת ה- C בתחום זה, כמו שימוש בקדם עיבוד (pre-processor). תמיד ניתן לנסות גישות אלטרנטיביות, ובהחלט יתכן שתמצא גישה טובה יותר מזו שמוצגת כאן.

 

2.5 תנאים מקדימים ותנאים מאוחרים

אף הגדרה פורמלית אינה מושלמת ללא תנאים מקדימים (pre-conditions) ותנאים מאוחרים (post-conditions). ניתן להתייחס לתנאים אלה, כאל יצירת חוזה בין האובייקט ללקוח. התנאים המקדימים מגדירים מצב של התוכנית, אשר הלקוח אחראי שיהיה נכון לפני הקריאה למתודה כלשהי, בעוד שהתנאים המאוחרים מגדירים את מצב התוכנית שמתודת האובייקט תהיה אחראית  ליצור לאחר היציאה ממנה.

 

גם במקרה זה, שפת C (שלא כמו Eiffel, למשל) אינה מספקת תמיכה רשמית לתנאים מוקדמים או מאוחרים. בכל אופן, הסטנדרט כן מגדיר פונקצית assert אשר יכולה (ואמורה!) לאפשר בדיקה של תנאים מוקדמים ומאוחרים [ עמוד ה-man  של פקודת

 ה- assert]. אנו נראה כיצד הדבר מתבצע כאשר נבחן את הביצוע של אובייקט אוסף הנתונים שלנו. לכן, תנאים אלה אמורים לבוא כהערות הנלוות להגדרת המתודה.

 

הוספת של התנאים לאובייקט אוסף הנתונים:

 

טען את collection.h

 

הערה:

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

 

טען את ucollection.h

 

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

 

2.6 מוסכמות C

ההגדרות המפורטות - אשר כל לקוח או משתמש באובייקט צריך לראות - ימוקמו לרוב בקובץ ה-  header, בעל הסיומת ".h". עבור אוסף הנתונים, אנו נמקם הגדרות אלו בקובץ collection.h וב-ucollection.h, ונשתמש בתכונות ה -#include של C על מנת לייבא אותם אל התוכניות הזקוקות להן. היישום עצמו או גוף המחלה, ימוקם בקובץ בעל סיומת ".c".

 

 

מושגים

 

טיפוס נתונים מופשטים (ADT)

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

 

 

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