Menu
Coddy logo textTech

תבניות לאי־שינוי

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C# של Coddy. שיעור 34 מתוך 70.

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

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

public class Point
{
    public readonly int X;
    public readonly int Y;
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

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

public Point Move(int deltaX, int deltaY)
{
    return new Point(X + deltaX, Y + deltaY);
}

הנקודה המקורית נשארת ללא שינוי, ובמקביל מתקבלת נקודה חדשה עם ערכים מעודכנים. דפוס זה משמש בכל רחבי ‎.NET‎ — מחרוזות, למשל, הן בלתי ניתנות לשינוי. כשקוראים ל־ToUpper(), מקבלים מחרוזת חדשה במקום לשנות את המקורית.

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

challenge icon

אתגר

קל

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

תארגנו את הקוד שלכם בשני קבצים:

  • Money.cs: צרו מחלקת Money בלתי־ניתנת לשינוי במרחב השמות Finance. לאובייקט הכסף שלכם יהיו:
    • שני שדות readonly: Amount (decimal) ו-Currency (string)
    • בנאי שמגדיר את שני הערכים — לאחר יצירתו, אובייקט Money לעולם לא יכול להשתנות
    • מתודה Add(decimal value) שמחזירה אובייקט Money חדש עם סכום מוגדל (באותו מטבע)
    • מתודה Subtract(decimal value) שמחזירה אובייקט Money חדש עם סכום מופחת (באותו מטבע)
    • מתודה Convert(string newCurrency, decimal rate) שמחזירה אובייקט Money חדש, שבו הסכום מוכפל בשער והמטבע הוא המטבע החדש
    • מתודה GetDisplay() שמחזירה מחרוזת בתבנית "{Amount} {Currency}"
    זכרו: אף אחת מהמתודות האלה לא אמורה לשנות את האובייקט המקורי — כולן מחזירות מופעי Money חדשים לגמרי!
  • Program.cs: בקובץ הראשי שלכם, הדגימו את אי־ההשתנות בפעולה. צרו אובייקט Money התחלתי, ואז בצעו עליו פעולות תוך שמירה של הפניות גם למקורי וגם לתוצאות. כך תוכיחו שהמקורי לעולם לא משתנה, גם לאחר כמה פעולות.

תקבלו ארבעה קלטים:

  • סכום התחלתי (decimal)
  • מטבע התחלתי (string)
  • סכום להוספה (decimal)
  • שער המרה ל-EUR (decimal)

הדפיסו את הפלט בתבנית הזאת:

Original: {GetDisplay()}
After add: {GetDisplay()}
Original unchanged: {GetDisplay()}
Converted: {GetDisplay()}

לדוגמה, אם הקלטים הם 100.00, USD, 50.00 ו-0.85, הפלט צריך להיות:

Original: 100.00 USD
After add: 150.00 USD
Original unchanged: 100.00 USD
Converted: 127.50 EUR

שימו לב שאובייקט Money המקורי עדיין מציג 100.00 USD גם לאחר קריאה ל-Add() — זוהי אי־השתנות! המתודה Add() יצרה אובייקט חדש לגמרי עם 150.00 USD, והשאירָה את המקורי ללא שינוי. ההמרה מתבצעת על תוצאת פעולת ההוספה.

נסו בעצמכם

using System;
using Finance;

class Program
{
    public static void Main(string[] args)
    {
        // קרא את נתוני הקלט
        decimal initialAmount = Convert.ToDecimal(Console.ReadLine());
        string initialCurrency = Console.ReadLine();
        decimal amountToAdd = Convert.ToDecimal(Console.ReadLine());
        decimal conversionRate = Convert.ToDecimal(Console.ReadLine());

        // TODO: צור אובייקט Money התחלתי עם הסכום והמטבע ההתחלתיים

        // TODO: הדפס את הסכום המקורי באמצעות GetDisplay()

        // TODO: קרא ל-Add() על האובייקט המקורי ושמור את התוצאה במשתנה חדש

        // TODO: הדפס את התוצאה לאחר ההוספה באמצעות GetDisplay()

        // TODO: הדפס שוב את הסכום המקורי כדי להוכיח שהוא לא השתנה

        // TODO: המר את התוצאה לאחר ההוספה ל-EUR באמצעות שער ההמרה

        // TODO: הדפס את הסכום לאחר ההמרה באמצעות GetDisplay()
    }
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר C# אונליין