כימוס

כּימוּס (או אֶנְקַפְּסוּלַצְיָה, מאנגלית: Encapsulation) הוא מאפיין חשוב בתכנות ובמיוחד בתכנות מונחה-עצמים המתייחס לאריזה של מידע עם הפעולות שפועלות על המידע הזה כיחידה אחת (קפסולה, או כמוסה בעברית). כימוס מאפשר יצירת יחידת תוכנה (מודול) בעלת ממשק מוגדר לשאר חלקי התוכנה. בדר"כ כימוס יכלול גם הסתרת מידע, דהיינו, הגבלה מפני גישה ישירה לחלק מרכיבי האובייקט שאינם שייכים לממשק המוגדר, כך שניתן לשנות רכיבים אלו מבלי שיהיה צורך לשנות את שאר חלקי התוכנה. בדרך כלל בשפות תכנות בעלות כימוס, אפשר להגדיר הרשאות גישה לאלמנטים שונים ביחידה. לדוגמה, ניתן להגדיר שפונקציה מסוימת ניתנת לגישה מכל מקום בקוד או רק מתוך הקוד של אותה יחידת תוכנה. הכימוס בא לענות על מספר בעיות: 1) חלקי קוד שניתן לראותם כחלק ממושג אחד מפוזרים במקומות רבים, כך שקשה להבין את טיפול הקוד במושג וקשה לשנות אותו. וכן קשה להעביר אותו לשימוש בפרויקטים אחרים. 2) בעיה של חוסר תיחום של קוד בתכנות פרוצדורלי, שבו משתנה אחד היה נגיש לכל מרחב הקוד, וכאשר נעשה בו שינוי או הוספו פונקציות שהתייחסו למשתנה שכבר קיים, הדבר גרם לכך שהקוד השתנה ללא בקרה, והשפיע על פונקציות אחרות ללא כל כוונה. הכימוס מסייע בבניית תוכנה בצורה מודולרית. מכיוון שליחידת התוכנה ממשק מוגדר, שאר הקוד אינו מסתמך על המימוש הפנימי שלה. הדבר מאפשר שינוי של המימוש הפנימי ללא שינוי של שאר התוכנה. יתרון נוסף של שימוש בכימוס הוא האפשרות להשתמש ביחידות תוכנה סגורות בלי צורך להבין את פרטי המימוש. יחידת התוכנה משמשת מעין "קופסה שחורה" או "קפסולה" עם מתגים. המשתמש צריך ללמוד רק איך להפעיל את המתגים ואינו צריך לדעת מה יש בתוך הקופסה. הדוגמה הבאה מדגימה איך כימוס מאפשר שינוי מימוש פנימי ללא שינוי הקוד החיצוני. הקוד הבא ב-C++‎ מתאר מחסנית הממומשת באמצעות תור סטנדרטי: #include <deque> class MyStack { private: std::deque<int> m_queue; public: void push(int x) { m_queue.push_front(x); } int pop() { int x = m_queue.front(); m_queue.pop_front(); return x; } }; ניתן לשנות את המימוש הפנימי לרשימה משורשרת: #include <list> class MyStack { private: std::list<int> m_list; public: void push(int a) { m_list.push_front(a); } int pop() { int a = m_list.front(); m_list.pop_front(); return a; } }; כל קוד שמשתמש במחסנית יפעל באופן זהה על שתי הגרסאות שלה. לדוגמה: #include <iostream> int main() { MyStack s; s.push(17); s.push(11); std::cout << s.pop() << std::endl; } למרות שההתנהגות תהיה זהה בשתי הגרסאות, ייתכנו הבדלים בזמני הריצה. כימוס אפשרי גם בשפות שאינו מונחות עצמים. בשפת C למשל, ניתן להגדיר מבנה (struct) בממשק הציבורי כ opaque data type באמצעות קובץ header יחד עם אוסף פונקציות שפועלות על ה-type וזאת מבלי שמסופקת למשתמש ה-type דרך לגשת לפריטי מידע או לפונקציות שמתכנן ה-type מעוניין להסתיר. // Header file "api.h" struct Entity; // Opaque structure with hidden members // API functions that operate on 'Entity' objects struct Entity * open_entity(int id); int process_entity(struct Entity *info); void close_entity(struct Entity *info); משתמשי ה-type יכולים להשתמש בפונקציות של האובייקט להקצאה של אובייקטים ולפעולות שהוגדרו עליהם בממשק הציבורי. התוכן והמימוש של ה-type נגיש אך ורק למימוש שלו. משתמשי ה-type אינם יכולים לגשת ישירות לתוכן שלו. קוד המימוש של קובץ ה-header מגדיר את התוכן של ה-type. // Implementation file "api.c" #include "api.h" struct Entity { int ent_id; // ID number char ent_name[20]; // Name ... and other members ... }; // API function implementations struct Entity * open_entity(int id) { ... } int process_entity(struct Entity *info) { ... } void close_entity(struct Entity *info) { ... } למרות שכימוס הוא מונח מתחום התוכנה, הרעיון של יצירת יחידה סגורה עם מימוש מוסתר וממשק מוגדר, אופייני לכל תחומי ההנדסה. לדוגמה, לגלגל של רכב משפחתי מסוג מסוים, יש ממשק ידוע של חיבור לרכב (לדוגמה, ארבעה ברגים בגודל ומרחקים מוגדרים). ישנם מספר יצרני גלגלים שנבדלים ביניהם בחומרים ובטכנולוגיה בה הם משתמשים. כל אותם גלגלים שומרים על אותו הממשק כלפי הרכב. העובדה שהם כולם עונים לאותו ממשק, היא זאת המאפשרת לבעל הרכב לבחור להרכיב כל אחד מהם. דוגמה נוספת: טלפון ביתי. יש הרבה מאוד סוגים של טלפונים ביתיים אבל לכולם יש אותו חיבור לקו החיצוני (במקרים מסוימים יש מספר קטן של חיבורים אפשריים). הממשק של הטלפון כלפי השקע שבקיר מוגדר היטב והוא זה שמאפשר למשתמשים לבחור את הטלפון החביב עליהם. נקודה חשובה בדוגמאות אלה היא שכל צד מסתמך רק על הגדרות הממשק ומשאיר את כל הפרטים האחרים כדרגות חופש. שקע הטלפון לדוגמה, מניח רק הנחות מסוימות על צורת התקע ועל פרוטוקול התקשורת. הוא אינו מניח דבר על החומרים מהם עשוי הטלפון, אופן מימוש הפרוטוקול וכו'. אי הסתמכות על דבר מלבד הממשק מאפשר שינוי של כל דרגות החופש ללא צורך בשינוי של השקע עצמו. הכימוס כולל הסתרת מידע שמטרתה היא להסתיר פרטי מימוש מקוד חיצוני. בהקשר זה, לא תמיד ברור אם קוד מחלקה יורשת נחשב פנימי או חיצוני. מצד אחד, ייתכן שקוד המחלקה היורשת נכתב על ידי מתכנת אחר שאינו בקיא בפרטי המימוש של מחלקת הבסיס. מצד שני, המשתנים של מחלקת הבסיס מהווים חלק מהמחלקה היורשת. שפות תכנות מסוימות, מאפשרות שליטה על רמת הכימוס ביחס למחלקה יורשת. ב-Java וב-C++‎ לדוגמה, ניתן להגדיר אלמנטים במחלקה כ-protected וכ-private. אף על פי ששתי הרשאות הגישה האלה, מסתירות את המימוש הפנימי מקוד חיצוני, רק הראשונה איננה מסתירה אותו ממחלקות יורשות. תכנות מונחה-עצמים מודול (תוכנה) Object-oriented programming concepts: Encapsulation, באתר אדובי מערכות כִּמּוּס במילון טכנולוגיית המידע: בינה מלאכותית (תשנ"ז), באתר האקדמיה ללשון העברית Rogers, Wm. Paul, Encapsulation is not information hiding, JavaWorld, ‏2001-05-18 לא תמיד כימוס כולל הסתרת מידע. למשל המחלקה Math בשפת ג'אווה היא קפסולה (כמוסה) שמכילה פעולות ללא מידע. בנוסף, תיתכן גם מחלקה עם מידע פשוט מאוד שאין תועלת בהסתרתו. למשל, אם יש לנו מחלקה כמו Point שמתארת נקודה במישור שהמידע שלה הוא שני מספרים ממשיים שהם הקואורדינטות שלה. זו דוגמה למידע פשוט שאין למחלקה תועלת בהסתרתו אלא רק לאורזו (עם הפעולות עליו) בקפסולה. הסתרת מידע פשוט כזה וחשיפתו דרך פעולות get/set שאינן תורמות כלום תיצור קוד מסורבל ללא תועלת.