תבניות עיצוב מבניות: Facade, Flyweight, ו-Proxy
תבניות עיצוב מבניות עוזרות לך לארגן מחלקות ואובייקטים.
היום נכסה את שלוש התבניות האחרונות כדי להשלים את ארגז הכלים שלך.
1. תבנית ה-Facade Facade מפשטת מערכות מורכבות. היא מספקת ממשק פשוט אחד לקבוצה של מחלקות מסורבלות.
דמיינו קולנוע. כדי לצפות בסרט, עליכם לעמעם את האורות, להפעיל את המקרן ולפתוח את הווילונות. במקום לקרוא לחמש מערכות שונות, אתם קוראים למתודה אחת: theater.watch_movie().
השתמשו בה כאשר:
- אתם רוצים לפשט תת-מערכת מורכבת.
- אתם זקוקים לנקודת כניסה אחת עבור API גדול.
- אתם רוצים להפריד בין הלקוחות (clients) לבין הלוגיקה הפנימית.
2. תבנית ה-Flyweight Flyweight חוסכת בזיכרון. היא עובדת כאשר יש לכם אלפי אובייקטים דומים.
במקום לאחסן כל פרט בכל אובייקט, אתם מפצלים את הנתונים. את הנתונים המשותפים והבלתי משתנים (intrinsic state) אתם שומרים במקום אחד. את הנתונים הייחודיים (extrinsic state) אתם שומרים בנפרד.
השתמשו בה כאשר:
- צריכת הזיכרון היא בעיה ממשית.
- אתם מנהלים מיליוני אובייקטים דומים, כמו תווים בעורך טקסט או חלקיקים במשחק.
- אתם רוצים להשתמש ב-object pooling כדי לשפר ביצועים.
3. תבנית ה-Proxy Proxy משמש כ"מחליף" (stand-in) עבור אובייקט אחר. הוא יושב בין הלקוח לבין האובייקט האמיתי כדי לשלוט בגישה.
Proxy יכול:
- לבצע Lazy load: טעינה עצלה של תמונות כבדות רק כאשר המשתמש לוחץ עליהן.
- לשלוט בגישה: בדיקה אם למשתמש יש הרשאה למחוק מסד נתונים.
- לרשום פעילות (Log): מעקב אחר מי משתמש בשירות מסוים.
- לשמור תוצאות בזיכרון מטמון (Cache): החזרת נתונים שמורים במקום להריץ לוגיקה יקרה.
השתמשו בה כאשר:
- אתם צריכים לדחות פעולות יקרות.
- אתם חייבים להגן על שירות רגיש.
- אתם רוצים להוסיף רישום (logging) או אבטחה מבלי לשנות את המחלקה המקורית.
טבלת סיכום
• Adapter: גורם למערכות לא תואמות לעבוד יחד. • Bridge: מפריד בין ההפשטה (abstraction) לבין המימוש (implementation). • Composite: בונה מבני עץ. • Decorator: מוסיף התנהגות מבלי לשנות מחלקות. • Facade: מפשט תת-מערכות מורכבות. • Flyweight: משתף נתונים כדי לחסוך בזיכרון. • Proxy: שולט בגישה לאובייקטים.
הכלל המוזהב: השתמש
Mahdi Shamlou: Structural Design Patterns 2026 - Facade, Flyweight, Proxy (דוגמאות ייצור)
תבניות עיצוב מבניות (Structural Design Patterns) עוסקות באופן שבו מחלקות ואובייקטים מורכבים ליצירת מבנים גדולים יותר. הן מסייעות ביצירת מערכות יעילות וגמישות.
1. תבנית Facade
תבנית ה-Facade מספקת ממשק פשוט יותר למערכת משנה (subsystem) מורכבת. במקום לתקשר עם מספר מחלקות, הלקוח (client) מתקשר עם מחלקת "Facade" אחת.
דוגמת ייצור: מערכת בית חכם
דמיינו בית חכם שבו יש מכשירים שונים: Light, Thermostat, ו-SecuritySystem.
class Light { turnOn() { console.log("Lights on"); } }
class Thermostat { setTemp(t: number) { console.log(`Temp set to ${t}`); } }
class SecuritySystem { arm() { console.log("Security armed"); } }
class SmartHomeFacade {
private light = new Light();
private thermostat = new Thermostat();
private security = new SecuritySystem();
leaveHome() {
this.light.turnOff(); // assuming turnOff exists
this.thermostat.setTemp(18);
this.security.arm();
}
}
2. תבנית Flyweight
תבנית ה-Flyweight משמשת לצמצום השימוש בזיכרון על ידי שיתוף כמה שיותר נתונים עם אובייקטים דומים אחרים.
דוגמת ייצור: מנוע משחק
במשחק, ייתכן שיש לכם אלפי עצים. שמירת הטקסטורה עבור כל עץ בנפרד היא בזבוז של זיכרון.
interface TreeType {
color: string;
texture: string;
}
class Tree {
constructor(private x: number, private y: number, private type: TreeType) {}
}
class TreeFactory {
private types: Map<string, TreeType> = new Map();
getTreeType(color: string, texture: string): TreeType {
const key = color + texture;
if (!this.types.has(key)) {
this.types.set(key, { color, texture });
}
return this.types.get(key)!;
}
}
3. תבנית Proxy
תבנית ה-Proxy מספקת תחליף (surrogate) או מציין מקום (placeholder) עבור אובייקט אחר כדי לשלוט בגישה אליו.
דוגמת ייצור: טעינת תמונות
טעינת תמונות ברזולוציה גבוהה עלולה להיות איטית. Proxy יכול לטעון את התמונה רק כאשר היא באמת נחוצה.
interface Image { display(): void; }
class RealImage implements Image {
constructor(private filename: string) { loadFromDisk(); }
private loadFromDisk() { console.log(`Loading ${this.filename}`); }
display() { console.log(`Displaying ${this.filename}`); }
}
class ProxyImage implements Image {
private realImage: RealImage | null = null;
constructor(private filename: string) {}
display() {
if (!this.realImage) {
this.realImage = new RealImage(this.filename);
}
this.realImage.display();
}
}