תבניות לאי־שינוי
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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(), מקבלים מחרוזת חדשה במקום לשנות את המקורית.
אובייקטים בלתי ניתנים לשינוי הם בטוחים מטבעם לשימוש במקביל, מכיוון ששום קוד לא יכול לשנות אותם לאחר יצירתם. קל יותר גם להבין את אופן פעולתם, כי ידוע שמצבם לעולם לא ישתנה באופן בלתי צפוי.
אתגר
קלבואו נבנה מערכת כסף בלתי־ניתנת לשינוי, המדגימה כיצד אפשר לתכנן אובייקטים כך שלעולם לא ישתנו לאחר יצירתם. תיצרו מחלקת 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()
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים
קבצים חיצונייםמרחבי שמות והנחיותמבוא למחלקות ולאובייקטיםמילת המפתח 'this'מתודות ופרמטריםשדות לעומת מאפייניםבנאיםאתחול אובייקטיםסיכום – מחשבון פשוט4ירושה
תחביר ירושה בסיסית (:)מילת המפתח 'base'מילות המפתח Virtual ו-Overrideמחלקות Sealedמחלקת הבסיס 'object'סיכום – היררכיית עובדים7תכונות מתקדמות
העמסת אופרטוריםאינדקסרים (this[])דריסת ToString()מתודות הרחבהסיכום – רשימה מותאמת אישית2מאפיינים וחברים סטטיים
מאפיינים במימוש אוטומטימאפיינים לקריאה בלבד ולכתיבה בלבדשדות ומתודות סטטייםמחלקות סטטיותחברים עם גוף ביטוי5פולימורפיזם וממשקים
פולימורפיזם בזמן קומפילציה לעומת בזמן ריצהממשק לעומת מחלקה מופשטתממשקים מרוביםממשקים מפורשיםהמרה כלפי מעלה והמרה כלפי מטהחזרה: מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה במקום ירושהGenerics (מחלקות ומתודות)נציגים ואירועיםמאפיינים ורפלקציהIDisposable והצהרת usingיסודות הזרקת תלויותתרגלו בעצמכם: קומפיילר C# אונליין