חלק V: מחלקות • פרק 10 • סעיפים 10.1–10.2 מנגנון ההפשטה החשוב ביותר ב-OOP

מחלקה כמנגנון הפשטה והפשטת עצמים: מה משותף ומה שונה?

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

🚗
🧠

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

💡 האנלוגיה המוחשית

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

⚖️ חוק הברזל הפדגוגי

בין עצמים מאותה מחלקה: הזהות (Identity) והמצב (State) שונים מעצם לעצם, בעוד שהפרוטוקול, מימוש ההתנהגות (המתודות) והמבנה (ההצהרות על משתני המופע) זהים לחלוטין!

🎯 תובנת המפתח

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

1. סעיף 10.1: מחלקה כמנגנון הפשטה — מדוע המחלקה חשובה יותר מהעצם עצמו?

הגדרת המבנה וההתנהגות במרוכז, ודוגמת סימולציית זרימת התנועה בכבישים

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

🤔

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

תחשוב על זה רגע: הפרדיגמה כולה נקראת "תכנות מונחה עצמים" (Object-Oriented Programming), והעצם הוא הישות המוחשית שחיה בזיכרון ומבצעת את החישוב בפועל בזמן הריצה. ובכל זאת, מדוע מושג ה"מחלקה" חשוב יותר ממושג ה"עצם"?

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

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

2. סעיף 10.2: הפשטת עצמים — מה משותף ומה שונה בין עצמים באותה מחלקה?

פירוק שלושת מאפייני היסוד של העצם (זהות, התנהגות, מצב ומבנה) ובחינת מקרה הקצה של ++C Stack

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

שונה בכל עצם ועצם! 🆔

1. הזהות (Identity)

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

זהה בכל עצמי המחלקה! ⚙️

2. ההתנהגות (Behavior): פרוטוקול ומימוש

ההתנהגות של כל העצמים מאותה מחלקה דומה ואף זהה בשני היבטים:
• הפרוטוקול (Protocol): אוסף ההודעות שהעצם מבין — זהה בכל העצמים של אותה מחלקה.
• מימוש ההתנהגות (Method Implementation): האופן שבו העצם מגיב בפועל להודעות (המתודות / קוד המתודות) — זהה גם הוא בכל העצמים שבאותה מחלקה.

לרוב שונה מעצם לעצם! 🎨

3א. המצב (State)

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

זהה בכל עצמי המחלקה! 📐

3ב. המבנה (Structure)

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

⚡

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

התשובה: כן ולא!

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

איך אפשר לבנות מחלקה שבה כל עצם בודד קובע לעצמו מימוש שונה של המתודות? זה אפשרי (אם כי לא סטנדרטי ואינו מומלץ). תסתכל על מימוש של מחסנית (Stack) בשפת ++C שבו במקום להגדיר מתודות רגילות במחלקה, אנחנו מגדירים את push ו-pop כמצביעים לפונקציות (Function Pointers):

class Stack {
    enum { N = 100 };
    int buff[N];
    int size;
public:
    void (*push)(int element);
    int (*pop)(void);
};

במימוש הזה, אף שכל המחסניות חולקות את אותה המחלקה Stack ואת אותו מבנה שדות (buff ו-size), כל מופע בודד של Stack יכול להצביע במשתנים push ו-pop שלו על פונקציות שונות לחלוטין בזמן ריצה!

עזר גרפי 10.1 • מחלקה כמנגנון הפשטה המוחל על עצמים

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

משותף מול ייחודי
🏛️ המחלקה Car (מנגנון ההפשטה) מקודדת את המשותף פעם אחת בלבד! ✓ מבנה זהה (Structure) הצהרות משתני מופע: color, size, speed ✓ פרוטוקול זהה (Protocol) אוסף ההודעות: drive(), stop(), turn() ✓ מימוש התנהגות זהה (Methods) קוד המתודות המגיב להודעות 🚗 עצם מכונית #1 (זהות ייחודית: @car_01) מצב (State) ייחודי: צבע = אדום | גודל = פרטי | מצב = בתנועה (90 קמ"ש) מבנה, פרוטוקול ומימוש התנהגות: זהים ומוחלים מהמחלקה Car! 🚙 עצם מכונית #2 (זהות ייחודית: @car_02) מצב (State) ייחודי: צבע = כחול | גודל = מסחרי | מצב = עומד (0 קמ"ש) מבנה, פרוטוקול ומימוש התנהגות: זהים ומוחלים מהמחלקה Car! 🚕 עצם מכונית #3 (זהות ייחודית: @car_03) מצב (State) ייחודי: צבע = צהוב | גודל = קומפקטי | מצב = בתנועה (55 קמ"ש) מבנה, פרוטוקול ומימוש התנהגות: זהים ומוחלים מהמחלקה Car!
🔍

מיני-סימולציה 10.1: סייר המאפיינים של עצמי המחלקה — מה משותף ומה שונה?

לחץ על כל מאפיין כדי לבדוק אם הוא משותף במחלקה או ייחודי לעצם
🕹️
סימולציה אינטראקטיבית גדולה 10.1

מעבדת הפשטת עצמים: סימולציית המכוניות ומחסנית המצביעים לפונקציות ב-++C

🛠️ ייצור מכונית חדשה מהמחלקה Car:

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

מופעי המכוניות החיים בסימולציית הכביש:
⚡

הגשר הנרטיבי: הבנו מה המחלקה מפשטת – אבל אילו תפקידים נוספים היא ממלאת ומאילו 5 איברים פנימיים היא בנויה?

ראינו שהמחלקה מרכזת את המבנה, הפרוטוקול ומימוש ההתנהגות של קבוצת עצמים. אבל מעבר להיותה מנגנון הפשטה, למחלקה יש עוד שלושה תפקידים קריטיים במערכת (תבנית ליצירת עצמים עם new, הצהרה על פרוטוקול לבדיקות טיפוסים בזמן הידור, וישות עצמאית בזמן ריצה), והיא בנויה מחמישה רכיבי יסוד מדויקים: כור יצירה (Forge), בית-חרושת (Mill), פרוטוקול (Protocol), התנהגות (Behavior) ומבנה (Structure). בוא נעבור ליחידה הבאה ונפרק את האנטומיה של המחלקה לגורמים!

סוף יחידה 10.1–10.2 • מחלקה כמנגנון הפשטה והפשטת עצמים
חלק V: מחלקות • פרק 10 • סעיפים 10.3–10.4 תפקידי המחלקה והאנטומיה הפנימית שלה

תפקידי המחלקה וחמשת רכיבי המבנה: Forge, Mill, Protocol, Behavior, Structure

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

🏗️
🧠

מודל מנטלי ועיקרון מנחה: הבחנה חדה בין "חוזה/חתימה" לבין "מימוש בפועל"

💡 האנלוגיה המוחשית

חשוב על מפעל ייצור מתקדם: יש בו קטלוג הזמנה ליצירת מוצר חדש (Forge) ופס ייצור שבונה את המוצר בפועל (Mill); ולאחר שהמוצר נוצר, יש לו לוח כפתורים חיצוני (Protocol), מנוע פנימי שמבצע את הפעולות (Behavior) ושלדה פנימית (Structure)!

⚖️ חוק הברזל הפדגוגי

במבנה המחלקה אנו מפרידים תמיד בין החתימה (Signature) לבין המימוש (Implementation): ביצירת עצמים — Forge (חתימות הבנאים) מול Mill (מימוש הבנאים); ובהתנהגות העצמים — Protocol (חתימות המתודות) מול Behavior (מימוש המתודות)!

🎯 תובנת המפתח

המחלקה ממלאת שלושה תפקידים נוספים מעבר להפשטה: (1) תבנית ליצירת עצמים בפקודת new; (2) הצהרה על פרוטוקול לבדיקות טיפוסים בזמן הידור; (3) ישות עצמאית המיוצגת לעיתים כעצם בזמן ריצה.

1. סעיף 10.3: שלושת תפקידי המחלקה (מעבר להיותה מנגנון הפשטה)

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

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

תפקיד 1 new ClassName()

1. תבנית ליצירת עצמים (Object Template)

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

תפקיד 2 Type Checking

2. הצהרה על פרוטוקול שהעצמים מבינים

המחלקה מגדירה את הטיפוס ואת אוסף ההודעות שהעצמים שלה מבינים. שפות שיש בהן הטפסה סטטית (טיפוסיות סטטית, כגון Java ו-++C) מסתמכות על הצהרה זו כדי לבצע בדיקות טיפוסים ולאתר שגיאות כבר בזמן הידור (Compile time)!

תפקיד 3 Runtime Object

3. ישות בפני עצמה בזמן ריצה

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

2. סעיף 10.4: מבנה של מחלקה — חמשת רכיבי היסוד

פירוק מדויק של מחלקה שלמה לחמישה מרכיבים: כור יצירה (Forge), בית-חרושת (Mill), פרוטוקול (Protocol), התנהגות (Behavior) ומבנה (Structure)

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

רכיב 1 · חתימות הבנאים

🔥 כור יצירה / מכרה עצמים (Forge)

אוסף ההודעות ליצירת עצמים חדשים — כלומר אוסף החתימות של הבנאים (Constructors Signatures) המגדירים אילו בקשות ליצירת עצם חדש ניתן להפנות למחלקה.

Forge = חתימות הבנאים
רכיב 2 · מימוש הבנאים

🏭 בית-חרושת לייצור עצמים (Mill)

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

Mill = מימוש הבנאים
רכיב 3 · חתימות המתודות

📡 פרוטוקול (Protocol)

אוסף ההודעות שהעצמים מבינים — כלומר החתימה של המתודות (Method Signatures) המהוות את הממשק החיצוני של כל עצם מהמחלקה.

Protocol = חתימת המתודות
רכיב 4 · מימוש המתודות

⚙️ התנהגות (Behavior)

המימוש של המתודות (Method Implementation) — גוף הקוד המורץ בפועל בתגובה לקבלת הודעה על ידי העצם.

Behavior = מימוש המתודות
רכיב 5 · משתני המופע

📐 מבנה (Structure)

אוסף משתני המופע (Instance Variables) — מבנה הזיכרון והתכונות הפנימיות המרכיבים כל עצם שנוצר מהמחלקה.

Structure = משתני המופע
עזר גרפי 10.2 • אנטומיה מלאה של מחלקה

חמשת רכיבי המבנה של מחלקה (10.4) ושלושת תפקידיה במערכת (10.3)

5 Components + 3 Roles
1. כור יצירה / מכרה עצמים (Forge) אוסף ההודעות ליצירת עצמים חדשים (חתימות הבנאים) משמש כתבנית ליצירת עצמים (למשל בביטוי new) 2. בית-חרושת לייצור עצמים (Mill) המימוש של הבנאים (גוף הקוד שמקצה ומייצר את העצם) בונה בפועל את המופע החדש בזיכרון 3. פרוטוקול (Protocol) אוסף ההודעות שהעצמים מבינים (חתימת המתודות) מאפשר לשפות עם הטפסה סטטית לאתר שגיאות בזמן הידור! 4. התנהגות (Behavior) המימוש של המתודות (גוף הקוד המגיב להודעות) קובע כיצד העצמים פועלים בזמן ריצה 5. מבנה (Structure) — אוסף משתני המופע (מבנה הזיכרון של כל עצם) + תפקיד היות המחלקה ישות בפני עצמה (ולעיתים מיוצגת בעצמה כעצם חי בזמן ריצה!)
🔬

מיני-סימולציה 10.2: סורק הרנטגן של 5 רכיבי המחלקה בקוד חי

לחץ על כל אחד מ-5 הרכיבים כדי להאיר אותו בתוך קוד המחלקה
⚙️
סימולציה אינטראקטיבית גדולה 10.2

מעבדת שלושת תפקידי המחלקה (10.3): תבנית new, בדיקת טיפוסים בהידור וישות בזמן ריצה

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

⚡

הגשר הנרטיבי: הכרנו את כל 5 רכיבי המחלקה – אבל מה קורה כששפת תכנות מחליטה להשמיט חלק מהם?

מחלקה מלאה כוללת כור יצירה (Forge), בית-חרושת (Mill), פרוטוקול (Protocol), התנהגות (Behavior) ומבנה (Structure). אבל בעולם האמיתי של שפות התכנות ישנם מבנים מרתקים שכוללים רק תת-קבוצה מתוך חמשת הרכיבים האלה! ביחידה הבאה (סעיף 10.5) ננתח ארבע דוגמאות חשובות בשפות שונות: מנשק (Interface) ב-Java, מחלקה אנונימית (Anonymous Class) ב-Java, מחלקה מופשטת (Abstract Class) ב-++C, ומחלקה שלמה (`Point`) בשפת Smalltalk!

חלק V: מחלקות • פרק 10 • סעיף 10.5 (10.5.1–10.5.4) ניתוח השוואתי של מנגנונים בשפות שונות

דוגמאות בשפות שונות: מנשק ומחלקה אנונימית ב-Java, מחלקה מופשטת ב-++C ומחלקת Point ב-Smalltalk

כיצד באים לידי ביטוי (או מושמטים) חמשת רכיבי המחלקה במנשק printable ב-Java, במחלקה אנונימית ב-Java, במחלקה מופשטת Shape ב-++C, ובמחלקה Point בסביבת Squeak ב-Smalltalk.

🌍
🧠

מודל מנטלי ועיקרון מנחה: כל מנגנון מיוחד בשפה הוא הפשטה שמדליקה או מכבה חלק מ-5 הרכיבים!

💡 מנשק ומחלקה אנונימית ב-Java

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

⚖️ מחלקה מופשטת ב-++C

מחלקה מופשטת (Abstract Class) כמו Shape (עם מתודה וירטואלית טהורה virtual void draw() = 0;) מאפשרת להגדיר בנאים, מתודות ומבנה, אך אינה מאפשרת ליצור עצמים חדשים ישירים מתוכה.

🎯 מחלקה ב-Smalltalk (Point)

בסביבת Squeak המחלקה Point מגדירה מבנה (instanceVariableNames: 'x y' הנגישים רק מתוך המחלקה) והתנהגות ופרוטוקול (מתודות המופע setX:setY:, x ו-y).

1. סעיפים 10.5.1–10.5.3: מנשק ומחלקה אנונימית ב-Java, ומחלקה מופשטת ב-++C

בחינה של מבנים חלקיים שמשמיטים חלק מחמשת רכיבי המחלקה

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

10.5.1 · מנשק בשפת Java

פרוטוקול טהור בלבד (interface)

מנשק בשפת Java מגדיר את הפרוטוקול (Protocol) אך לא את ההתנהגות (Behavior), אינו מגדיר מבנה שדות מופע, ואינו מאפשר ליצור עצמים חדשים (חסר Forge ו-Mill):

interface printable {
    void print();
}
10.5.2 · מחלקה אנונימית ב-Java

מופע יחיד ללא שכפול נוסף

מחלקה אנונימית מגדירה מבנה (private int s), אתחול חד-פעמי (s = 3), פרוטוקול והתנהגות (print()), אך אינה מאפשרת ליצור עצמים נוספים של המחלקה מעבר למופע הבודד p:

printable p = new printable {
    private int s;
    { s = 3; }
    void print() {
        System.out.println(s);
    }
};
10.5.3 · מחלקה מופשטת ב-++C

ללא יצירת עצמים ישירים

מחלקה מופשטת ב-++C (הכוללת מתודה וירטואלית טהורה = 0) אינה מאפשרת ליצור עצמים חדשים ישירים, אך ניתן להגדיר בה בנאים (עבור תת-מחלקות), מתודות ומבנה:

class Shape {
    virtual void draw() = 0;
    // pure virtual method
};

2. סעיף 10.5.4: מחלקה בסמולטוק — המחלקה Point בסביבת Squeak

הגדרת המבנה (instanceVariableNames: 'x y') והגדרת ההתנהגות והפרוטוקול (setX:setY:, x, y)

איך מוגדרת מחלקה מלאה בשפת Smalltalk? בסביבת העבודה Squeak, מחלקה מוגדרת באמצעות עורך מיוחד (ה-System Browser). בוא נתבונן בהגדרת המחלקה Point (נקודה דו-ממדית) ונראה כיצד מוגדרים בה המבנה וההתנהגות:

📐 1. הגדרת המבנה (Structure) של Point: instanceVariableNames

המחלקה מגדירה את המבנה של עצמים באמצעות רשימה מפורטת של שמות משתני-מופע (instanceVariableNames). במחלקה Point מוגדרים שני משתני מופע: x ו-y. משתנים אלה נגישים אך ורק מתוך קוד המחלקה עצמה!

Object subclass: #Point
    instanceVariableNames: 'x y'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Graphics-Primitives'
⚙️ 2. הגדרת ההתנהגות והפרוטוקול (מתודות מופע): Instance Methods

המתודה הראשונה מגדירה הודעה setX:setY: שלוקחת שני עצמים כארגומנטים ושומרת אותם במשתני המופע x ו-y. בנוסף מוגדרות ההודעות x ו-y שכאשר העצם מקבל אותן, הוא מחזיר (^) את העצם השמור במשתנה המופע המתאים:

setX: xValue setY: yValue
    x := xValue.
    y := yValue

x
    "Answer the x coordinate."
    ^x

y
    "Answer the y coordinate."
    ^y
עזר גרפי 10.3 • טבלת השוואה ויזואלית של מקרי המבחן (10.5.1–10.5.4)

אילו מרכיבים ויכולות קיימים במנשק Java, במחלקה אנונימית, במחלקה מופשטת ב-++C ובמחלקת Smalltalk?

Cross-Language Matrix
המבנה והשפה (סעיף 10.5) פרוטוקול (Protocol) התנהגות (Behavior) מבנה שדות (Structure) יצירת עצמים חדשים 10.5.1 מנשק Java (printable) ✓ כן (print) ✗ לא ✗ לא ✗ לא מאפשר 10.5.2 מחלקה אנונימית ב-Java ✓ כן ✓ כן (print) ✓ כן (int s) ⚠️ מופע יחיד בלבד! 10.5.3 מחלקה מופשטת ++C (Shape) ✓ כן (draw=0) ✓ אפשרי (+בנאים) ✓ אפשרי ✗ לא ישירות 10.5.4 מחלקה ב-Smalltalk (Point) ✓ מלא (x, y, setX:setY:) ✓ מלא (מתודות מופע) ✓ מלא ('x y' כמוסים) ✓ אינספור מופעים!
🧭

מיני-סימולציה 10.3: בוחן הרכיבים החסרים והקיימים ב-4 הדוגמאות של סעיף 10.5

לחץ על כל דוגמה כדי לבדוק את מאזן 5 הרכיבים שלה
🖥️
סימולציה אינטראקטיבית גדולה 10.3

מעבדת ה-System Browser ב-Squeak: המחלקה Point, כמוסת השדות ומתודות המופע

חקור את הגדרת המחלקה Point בסביבת Squeak ושלח הודעות אל מופע נקודה חי: נסה לעדכן את הקואורדינטות באמצעות setX:setY:, לקרוא אותן באמצעות ההודעות x ו-y, או לבדוק מה קורה כשמנסים לגשת ישירות למשתני המופע מבחוץ!

Squeak Workspace — שלח הודעות לעצם aPoint:
מצב העצם aPoint בזיכרון (Structure + State): x = 15 , y = 30
⚡

הגשר הנרטיבי: ראינו איך מתודות המופע של Point פועלות – אבל מי בעצם מייצר את העצם החדש כשאנחנו שולחים הודעה למחלקה Point עצמה?

בסעיף 10.5.4 ראינו את מתודת המופע setX:setY: שמאתחלת נקודה קיימת. אבל איך נוצרת הנקודה מלכתחילה? כאן אנחנו מגיעים אל הרעיון העמוק ביותר בפרק 10 (סעיף 10.6): מחלקה כעצם (Class as an Object)! ביחידה הבאה נגלה כיצד ב-Smalltalk המחלקה Point עצמה היא עצם חי בזיכרון שמקבל את ההודעה x:y:, שולח לעצמו self basicNew ומאתחל את המופע באמצעות setX:setY:, ונשווה זאת למנגנון הבבואה / השתקפות (Reflection) בשפת Java (getClass(), getField(), set()) ולחידת השרשור o.getClass().getClass().getClass()!

חלק V: מחלקות • פרק 10 • סעיף 10.6 ייצוג מחלקות בזמן ריצה (Runtime Representation)

מחלקה כעצם: מתודות מחלקה ב-Smalltalk מול מנגנון הבבואה (Reflection) ב-Java

איך המחלקה Point ב-Smalltalk פועלת כעצם המגיב להודעה x:y: עם self basicNew, כיצד מתשאלים מחלקות חיות ב-Squeak, ואיך פועל מנגנון ההשתקפות ב-Java (getClass, getField, set) כולל שרשור קריאות getClass().

🪞
🧠

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

💡 האנלוגיה המוחשית

בגישה מסורתית, המחלקה היא כמו תבנית אפייה שנזרקת לפח ברגע שהעוגיות מוכנות. בגישה המתקדמת של OOP, המחלקה היא "שף חי" שעומד במטבח בזמן הריצה — אפשר לשלוח לו הודעות, לבקש ממנו לייצר מופע חדש ולשאול אותו אילו שדות ומתודות יש לו!

⚖️ חוק הברזל ב-Smalltalk

ב-Smalltalk מחלקות הן עצמים לכל דבר (First-Class Objects). כאשר שולחים למחלקה Point את ההודעה x:y:, המשתנה self בתוך מתודת-המחלקה הוא עצם המחלקה Point עצמו, שיוצר מופע חדש (self basicNew) ומאתחלו ב-setX:setY:!

🎯 תובנת המפתח ב-Java

ב-Java מנגנון הבבואה (Reflection) מייצג כל מחלקה בזמן ריצה כעצם מטיפוס java.lang.Class. לכן o.getClass() מחזירה עצם, ושרשרת קריאות o.getClass().getClass()... מגיעה כבר בצעד השני לנקודת שבת: Class.class!

1. מחלקה כעצם בשפת Smalltalk — מתודת המחלקה x:y: ותשאול חי ב-Squeak

איך עצם המחלקה Point מקבל הודעות, מייצר מופע חדש עם basicNew ומאתחל אותו עם setX:setY:

בפרדיגמות תכנות מסורתיות, מחלקה נתפסת כישות שקיימת בזמן הידור בלבד (Compile-time construct) — מעין תבנית סטטית שנעלמת לאחר יצירת הקובץ הבינארי. לעומת זאת, בעיצוב מתקדם של תכנות מונחה עצמים, המחלקה עצמה קיימת בזמן ריצה כעצם חי בזיכרון! עיקרון זה בא לידי ביטוי בשתי השפות המרכזיות שלנו — באופן טהור ומובנה ב-Smalltalk, ובאמצעות מנגנון הבבואה/השתקפות (Reflection) ב-Java.

ב-Smalltalk, מחלקות הן בעצמן גם עצמים לכל דבר ועניין (First-Class Objects) המגיבים להודעות. הודעות שנשלחות ישירות אל עצם המחלקה נקראות מתודות מחלקה (Class Methods). בוא נסתכל על מתודת-המחלקה המפורסמת של המחלקה Point ליצירת נקודה חדשה (Point class>>x:y:):

x: xInteger y: yInteger
    "Answer an instance of me with coordinates xInteger and yInteger."
    ^self basicNew setX: xInteger setY: yInteger

בוא נפרק יחד את הקסם שמתרחש בשורה הזו כאשר אנחנו שולחים למחלקה Point את ההודעה Point x: 10 y: 20:

1. מי הוא המקבל self?

מכיוון שההודעה x: 10 y: 20 נשלחה ישירות אל המחלקה Point, המשתנה self בתוך מתודת-המחלקה הוא עצם המחלקה Point עצמו!

2. מה עושה self basicNew?

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

3. מה עושה setX: xInteger setY: yInteger?

מיד לאחר ש-self basicNew החזיר את המופע החדש שנוצר, נשלחת אל המופע החדש הזה הודעת המופע setX:setY: (שראינו בסעיף הקודם!) כדי לאתחל את משתני המופע x ו-y שלו.

4. מה עושה סימן ההחזרה ^?

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

🔎 תשאול וחקירת מחלקות בזמן אמת בסביבת Squeak

מאחר שב-Smalltalk המחלקה Point היא עצם חי בזיכרון, ניתן לפתוח עליה חלון חקירה (Point inspect) ולשלוח אליה הודעות תשאול בזמן אמת:
• Point subclasses — מחזיר מערך של כל תת-המחלקות היורשות מ-Point.
• Point allSuperclasses — מחזיר את שרשרת כל מחלקות-העל של Point.
• Point instVarNames — מחזיר את שמות משתני המופע המוגדרים במחלקה: #('x' 'y').
• Point selectors — מחזיר אוסף של כל שמות המתודות (הסלקטורים) שהמחלקה מבינה.

2. ייצוג מחלקה כעצם ב-Java (Reflection) ושרשרת getClass()

המתודה assignField עם getClass, getField ו-set, וניתוח נקודת השבת של o.getClass().getClass().getClass()

העיקרון שלמחלקות יש ייצוג מוחשי בזמן ריצה מצוי גם בשפת Java (באמצעות מנגנון הבבואה / ההשתקפות – Reflection, דרך אובייקטים מטיפוס java.lang.Class). בשפת Java אפשר: (1) לתשאל עצם בזמן ריצה לגבי מחלקתו באמצעות o.getClass(); (2) לתשאל את המחלקה בזמן ריצה לגבי שדה המוגדר בה באמצעות c.getField(fieldName); (3) ולהציב בשדה זה ערך חדש בזמן ריצה באמצעות f.set(o, value)!

void assignField(Object o, String fieldName, Object value) {
    Class c = o.getClass();
    Field f = c.getField(fieldName);
    f.set(o, value);
}
❓

אתגר חשיבה 1: האם getClass() ב-Java מחזירה עצם?

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

✅ הפתרון וההסבר המדויק: • כן, המתודה getClass() מחזירה עצם לכל דבר! העצם המוחזר הוא מופע של המחלקה java.lang.Class (שבעצמה יורשת מ-java.lang.Object).
• העובדה שניתן להפעיל עליו את המתודה c.getField(...) היא ההוכחה הישירה לכך שמדובר בעצם המממש פרוטוקול מתודות.
• ומאחר שהערך המוחזר c הוא עצם, בהחלט ניתן לקרוא עליו שוב למתודה getClass() (כלומר c.getClass())!
🔄

אתגר חשיבה 2: מה מחזירה שרשרת הקריאות o.getClass().getClass().getClass()...?

עבור עצם o של מחלקה כלשהי ב-Java, מה תחזיר הקריאה המשורשרת o.getClass().getClass().getClass() (ואף קריאה רביעית .getClass())?

✅ מעקב צעד-אחר-צעד ונקודת השבת: 1. o.getClass(): מחזיר את אובייקט המחלקה של o (למשל String.class — שהוא מופע מסוג Class).
2. o.getClass().getClass(): מהי המחלקה של אובייקט מסוג Class? המחלקה של כל אובייקט Class ב-Java היא המחלקה java.lang.Class עצמה (Class.class)!
3. o.getClass().getClass().getClass() (וכן קריאה רביעית והלאה): מהי המחלקה של Class.class? שוב java.lang.Class!
💡 מסקנה: ב-Java מגיעים לנקודת שבת (Fixed Point / מעגל סגור) כבר ברמה השנייה! (בניגוד ל-Smalltalk שבה שרשרת שליחת ההודעה class עוברת דרך מטה-מחלקות עד למעגל מחזורי בין Metaclass ל-Metaclass class).
עזר גרפי 10.4 • מחלקה כעצם בזמן ריצה (סעיף 10.6)

יצירת נקודה ב-Smalltalk (Point x:y:) מול מנגנון הבבואה ונקודת השבת של getClass() ב-Java

Runtime Class Objects
1. מחלקה כעצם ב-Smalltalk (Point x: 10 y: 20) שלב א': העצם Point מקבל את ההודעה x: 10 y: 20 בתוך מתודת המחלקה: self == עצם המחלקה Point עצמו! שלב ב': self basicNew (הקצאת מופע חדש בזיכרון) מתודה פרימיטיבית ממחלקת התשתית Behavior שלב ג': setX: 10 setY: 20 והחזרת המופע (^) אתחול המופע החדש והחזרתו לשולח כתשובה להודעה! 2. בבואה ב-Java ונקודת השבת של getClass() העצם o (למשל "Hello") getClass() ➔ String.class (מופע של Class) getClass() ➔ Class.class java.lang.Class 🔄 נקודת שבת! בבואה (Reflection) בזמן ריצה ב-Java: Class c = o.getClass(); Field f = c.getField(fieldName); f.set(o, value);
🔗

מיני-סימולציה 10.4: מדגים שרשרת הקריאות o.getClass().getClass()...

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

מעבדת "מחלקה כעצם": Point class>>x:y: ב-Smalltalk מול assignField (Reflection) ב-Java

💎 א. שלח הודעות אל עצם המחלקה Point ב-Smalltalk:
☕ ב. הרץ את assignField(o, fieldName, value) ב-Java Reflection:
⚡

הגשר הנרטיבי: השלמנו את כל סעיפי פרק 10 (10.1 עד 10.6) – בוא נסכם ונתרגל את כל מושגי הפרק!

עברנו יחד מסע שלם ומעמיק: מהגדרת המחלקה כמנגנון ההפשטה החשוב ביותר ב-OOP (10.1), דרך פירוק זהות, התנהגות, מצב ומבנה (10.2), שלושת תפקידי המחלקה וחמשת רכיבי המבנה שלה (10.3–10.4), מקרי המבחן בשפות השונות (10.5), ועד למחלקה כעצם חי בזמן ריצה ב-Smalltalk וב-Java (10.6). עכשיו נעבור ליחידה המסכמת של פרק 10 שבה נבחן את עצמנו בכרטיסיות זיכרון תלת-ממדיות (3D Flashcards) ונתכונן לפרק 11!

חלק V: מחלקות • פרק 10 • יחידה מסכמת אינטגרציה ואינדקס מושגים

מילון מושגים אינטראקטיבי וסיכום פרק 10 (המושג "מחלקה")

חיבור כל חלקי הפאזל של פרק 10: מחלקה כמנגנון הפשטה, הפשטת עצמים, 3 תפקידי המחלקה, 5 רכיבי המבנה (Forge, Mill, Protocol, Behavior, Structure), דוגמאות בשפות שונות ומחלקה כעצם חי בזמן ריצה.

🏆
🧠

מודל מנטלי ועיקרון מנחה: שלוש עדשות ההסתכלות על המושג "מחלקה"

💡 עדשה 1: הפשטת עצמים (10.1–10.2)

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

⚖️ עדשה 2: תפקידים ורכיבים (10.3–10.5)

למחלקה 3 תפקידים (תבנית new, חוזה לבדיקת טיפוסים בהידור, וישות בזמן ריצה) ו-5 רכיבים (Forge, Mill, Protocol, Behavior, Structure) שמופיעים במלואם או בחלקם במבנים שונים.

🎯 עדשה 3: מחלקה כעצם (10.6)

המחלקה מיוצגת כעצם חי בזמן ריצה — הן ב-Smalltalk (מתודת המחלקה x:y: עם self basicNew) והן ב-Java Reflection (getClass(), getField(), set() ונקודת השבת Class.class).

עזר גרפי 10.5 • מפת הסינתזה השלמה של פרק 10

מבט-על על המושג "מחלקה" (סעיפים 10.1 עד 10.6)

Chapter 10 Synthesis
10.1–10.2 הפשטת עצמים • מנגנון ההפשטה החשוב ביותר • זהה: פרוטוקול, מתודות, מבנה • שונה: זהות (Identity) ומצב • חריג: Stack ב-++C עם מצביעים לפונקציות push/pop 10.3–10.4 תפקידים ומבנה • 3 תפקידים: תבנית new, בדיקת טיפוסים, ישות חיה • 5 רכיבים: Forge, Mill, Protocol, Behavior, Structure (משתני מופע) 10.5 דוגמאות בשפות • Java interface printable • מחלקה אנונימית (מופע יחיד) • ++C Shape (draw() = 0) • Smalltalk Point ('x y', setX:setY:, ^x, ^y) 10.6 מחלקה כעצם • Smalltalk: Point x:y: ^self basicNew setX:setY: • Java Reflection: getClass, getField, set • נקודת שבת: Class.class
⚡

מיני-סימולציה 10.5: בוחן אבחנה מהיר — בדוק את שלוש האבחנות הקריטיות של פרק 10!

לחץ על כל דילמה כדי לבדוק את האבחנה המדויקת
🃏
סימולציה אינטראקטיבית גדולה 10.5

כרטיסיות זיכרון תלת-ממדיות (3D Flashcards) לכל 12 מושגי פרק 10

לחץ על כל כרטיסייה כדי להפוך אותה ולבחון את עצמך על כל אחד מ-12 מושגי היסוד של פרק 10:

נחשפו: 0 / 12
🚀

סיימנו במלואו את פרק 10 (המושג "מחלקה")! מה מחכה לנו בפרק 11?

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

🎯 התחנה הבאה שלנו – פרק 11: מחלקות בסיסיות ב-Squeak: בפרק 11 נכיר מקרוב את ספריית המחלקות העשירה של Squeak, ונפתח בסעיף 11.1: מחלקת הבסיס Object (מתודות הדפסה printOn: ו-printString, מתודות שוויון וזהות =, == ו-hash, בדיקות שייכות למחלקה isKindOf:, isMemberOf: ו-respondsTo:, העתקה ושכפול copy, shallowCopy ו-postCopy, וטיפול בשגיאות subclassResponsibility, shouldNotImplement, error: ו-halt)!
🕹️

מעבדת חקר אינטראקטיבית

❓

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

🚀 התחנה הבאה שלנו: פרק 11 – מחלקות בסיסיות ב-Squeak

השלמנו במלואו את פרק 10: המושג "מחלקה" (סעיפים 10.1–10.6)! התחנה הבאה שלנו בחלק V היא פרק 11: מחלקות בסיסיות ב-Squeak, הפותח בסעיף 11.1: מחלקת הבסיס Object: