# תבניות עיצוב (Design Patterns)📖 שיעור

## מה זה?

`DiscountStrategy` ו-`PaymentMethod` מהשיעורים הקודמים לא היו רק "דוגמאות ל-OCP" — הן דוגמאות לתבנית עיצוב מוכרת בשם **Strategy**. תבניות עיצוב (Design Patterns) הן פתרונות חוזרים ומוכרים לבעיות תכנון שחוזרות על עצמן בהרבה מערכות שונות — יש להן שם משותף כדי שמפתחים יוכלו לתקשר ("תשתמש כאן ב-Factory") בלי להסביר את כל הפתרון מאפס בכל פעם. השיעור הזה נותן שם לדפוסים שכבר נתקלנו בהם, ומוסיף שניים נוספים שכיחים במיוחד.

## מילות מפתח שחשוב לזכור

• Design Pattern — פתרון מוכר וחוזר לבעיית תכנון נפוצה, עם שם מוסכם

• Strategy Pattern — כמה אלגוריתמים/התנהגויות חלופיות מאחורי Interface משותף, ניתנות להחלפה בזמן ריצה (כבר תרגלנו: `DiscountStrategy`)

• Singleton Pattern — מבטיח שקיים מופע **יחיד** בלבד ממחלקה מסוימת בכל התוכנית, עם נקודת גישה גלובלית אליו

• Factory Pattern — מרכז את לוגיקת ה-**יצירה** של אובייקטים במקום אחד, במקום `new` מפוזר בכל הקוד

```csharp
// Strategy — we already practiced this! Now it has a name
// (see DiscountStrategy / PaymentMethod from previous lessons)
interface IDiscountStrategy { double Apply(double price); }
```

```csharp
// Singleton — a single global instance
class AppConfig
{
    private static AppConfig _instance;
    private AppConfig() { }   // private constructor — new AppConfig() from outside is not allowed

    public static AppConfig Instance => _instance ??= new AppConfig();

    public string Environment { get; set; } = "Production";
}

AppConfig.Instance.Environment = "Staging";
Console.WriteLine(AppConfig.Instance.Environment);   // Staging — the same instance everywhere in the program
```

```csharp
// Factory — centralizes complex creation logic in one place
interface INotifier { void Notify(string message); }
class EmailNotifier : INotifier { public void Notify(string m) => Console.WriteLine($"Email: {m}"); }
class SmsNotifier : INotifier { public void Notify(string m) => Console.WriteLine($"SMS: {m}"); }

static class NotifierFactory
{
    public static INotifier Create(string type) => type switch
    {
        "email" => new EmailNotifier(),
        "sms" => new SmsNotifier(),
        _ => throw new ArgumentException("Unsupported type")
    };
}

INotifier notifier = NotifierFactory.Create("email");   // calling code doesn't need to know which types exist
```

## הסבר עיקרי

Strategy הוא בדיוק OCP במימוש — כבר בנינו אותו פעמיים (הנחות, אמצעי תשלום) בלי לדעת שיש לו שם. הרעיון תמיד זהה: הגדר Interface/abstract class משותף, ותן לקוד הקורא לבחור בזמן ריצה איזה מימוש להשתמש בו — בלי `if`/`switch` מפושט בקוד הלוגי עצמו.

Singleton פותר בעיה ספציפית — הגדרות אפליקציה גלובליות, חיבור יחיד למסד נתונים, לוגר משותף — כל אלה מקרים שבהם **חייב** להיות מופע יחיד, כי כמה מופעים היו יוצרים חוסר-עקביות (שתי הגדרות שונות בו-זמנית). `??=` (Null-Coalescing Assignment, מוכר מה-`??` שכבר למדנו) יוצר את המופע רק בפעם הראשונה שמבקשים אותו — Lazy Initialization.

Factory מפריד "מה ליצור" מ"איך משתמשים בזה" — בלי Factory, `NotifierFactory.Create("email")` היה מוחלף ב-`new EmailNotifier()` מפוזר בכל מקום שצריך Notifier — קשה להחליף לוגיקת בחירה בעתיד. עם Factory, כל לוגיקת ה"איזה מימוש ליצור" יושבת במקום אחד — קוד קורא רק מבקש "תן לי notifier מסוג email", בלי לדעת אילו מחלקות בכלל קיימות.

## יתרונות

תבניות עיצוב נותנות שפה משותפת בין מפתחים — "תשתמש ב-Factory כאן" מתקשר מיידית פתרון שלם; Strategy מאפשר החלפת התנהגות בזמן ריצה בלי `if`/`switch` מפושט; Singleton מבטיח עקביות למשאב גלובלי יחיד; Factory מרכז לוגיקת יצירה מורכבת במקום נגיש אחד.

## חסרונות

Singleton בשימוש-יתר הופך לעיתים ל"מצב גלובלי מוסתר" שקשה לבדוק (Unit Test) ולעקוב אחריו; שימוש בתבנית עיצוב "כי אפשר" במקום כי היא באמת פותרת בעיה קונקרטית מוסיף מורכבות מיותרת — Over-Engineering.

## נקודות חשובות

• Strategy = אלגוריתמים חלופיים מאחורי חוזה משותף, נבחר בזמן ריצה

• Singleton = מופע יחיד גלובלי, קונסטרוקטור `private`, גישה דרך Property/מתודה סטטית

• Factory = לוגיקת יצירת אובייקטים מרוכזת במקום אחד, לא `new` מפוזר

• תבניות עיצוב הן פתרונות **חוזרים** לבעיות **נפוצות** — לא כל קוד צריך תבנית מוכרת

## טעויות נפוצות

• להשתמש ב-Singleton לכל דבר "כדי שיהיה גלובלי", גם כשזה לא באמת נדרש — מקשה על בדיקות

• לשכוח קונסטרוקטור `private` ב-Singleton, ולאפשר בטעות כמה מופעים עם `new`

• להזדקק לתבנית עיצוב "כי זה נשמע מקצועי" בלי בעיה קונקרטית שהיא פותרת

## סיכום

תבניות עיצוב הן פתרונות מוכרים לבעיות תכנון חוזרות, עם שם משותף שמפתחים משתמשים בו לתקשורת מהירה. Strategy (שכבר תרגלנו כ-OCP) מחליף אלגוריתמים בזמן ריצה מאחורי חוזה משותף; Singleton מבטיח מופע יחיד גלובלי למשאב שחייב עקביות; Factory מרכז לוגיקת יצירה מורכבת במקום אחד. כמו כל כלי — משתמשים בהן כשיש בעיה קונקרטית שהן פותרות, לא כברירת מחדל.

## דוקומנטציה רשמית

[Refactoring.Guru — Design Patterns](https://refactoring.guru/design-patterns)

[Microsoft Learn — Singleton pattern implementation in .NET](https://learn.microsoft.com/dotnet/core/extensions/generic-host#singleton-services)

---

## תרגילים

### תרגיל 1 — Singleton בסיסי

**המשימה:** בנו `class Logger` עם Singleton — מתודה `Log(string message)` שמדפיסה עם חותמת זמן. השתמשו בו משני מקומות שונים בקוד וודאו ששניהם משתמשים באותו מופע בדיוק.

**בדיקה:** `Logger.Instance` מוחזר זהה משני מקומות שונים בקוד (אפשר לוודא עם `ReferenceEquals`).

### תרגיל 2 — Factory

**המשימה:** בנו `ShapeFactory` שמקבלת מחרוזת ("circle"/"rectangle") ומחזירה מופע `Shape` מתאים (מהיחידה הקודמת), בלי שהקוד הקורא כותב `new` ישירות בכלל.

**בדיקה:** `ShapeFactory.Create("circle")` מחזירה `Circle` תקין; קלט לא נתמך זורק שגיאה ברורה.

---

## פרויקט מסכם

**המשימה:** הכינו מצגת קצרה (5 דקות) על תבנית עיצוב אחת **נוספת** שלא נלמדה בכיתה (למשל: Observer, Decorator, Adapter, או Builder), כולל דוגמת קוד ב-C#.

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

**בדיקה:** קטע הקוד מתקמפל ורץ בפועל; ההסבר כולל דוגמה קונקרטית, לא רק הגדרת מילון.

## מה בפרק הבא

בפרק הבא נעבור ליחידה החדשה — **Exceptions, IO, JSON ו-LINQ**: כלים מעשיים לעבודה עם קבצים, נתונים מובְנים וסינון/עיבוד אוספים, שיובילו לפרויקט המסכם הראשון של הקורס.
