שלטו בתבניות עיצוב ב-JavaScript עם מדריך היישום המלא שלנו. למדו תבניות יצירה, מבנה והתנהגות עם דוגמאות קוד מעשיות.
תבניות עיצוב ב-JavaScript: מדריך יישום מקיף למפתחים מודרניים
מבוא: התוכנית לקוד חסין ועמיד
בעולם הדינמי של פיתוח תוכנה, כתיבת קוד שפשוט עובד היא רק הצעד הראשון. האתגר האמיתי, וסימן ההיכר של מפתח מקצועי, הוא יצירת קוד שהוא סקיילבילי, קל לתחזוקה וקל להבנה ושיתוף פעולה עבור אחרים. כאן נכנסות לתמונה תבניות עיצוב (Design Patterns). הן אינן אלגוריתמים או ספריות ספציפיות, אלא תוכניות-על (blueprints) ברמה גבוהה, שאינן תלויות שפה, לפתרון בעיות חוזרות בארכיטקטורת תוכנה.
עבור מפתחי JavaScript, הבנה ויישום של תבניות עיצוב חיוניים יותר מתמיד. ככל שיישומים גדלים במורכבותם, ממסגרות פרונט-אנד מורכבות ועד שירותי בק-אנד רבי עוצמה על Node.js, בסיס ארכיטקטוני מוצק אינו נתון למשא ומתן. תבניות עיצוב מספקות את הבסיס הזה, ומציעות פתרונות שנבחנו בקרב ומקדמים צימוד רופף (loose coupling), הפרדת אחריויות (separation of concerns) ושימוש חוזר בקוד.
מדריך מקיף זה יוביל אתכם דרך שלוש הקטגוריות הבסיסיות של תבניות עיצוב, ויספק הסברים ברורים ודוגמאות יישום מעשיות ומודרניות ב-JavaScript (ES6+). מטרתנו היא לצייד אתכם בידע לזהות איזו תבנית להשתמש עבור בעיה נתונה וכיצד ליישם אותה ביעילות בפרויקטים שלכם.
שלושת עמודי התווך של תבניות העיצוב
תבניות עיצוב מסווגות בדרך כלל לשלוש קבוצות עיקריות, כל אחת מהן מתמודדת עם קבוצה נפרדת של אתגרים ארכיטקטוניים:
- תבניות יצירה (Creational Patterns): תבניות אלו מתמקדות במנגנוני יצירת אובייקטים, ומנסות ליצור אובייקטים באופן המתאים למצב. הן מגבירות את הגמישות והשימוש החוזר בקוד קיים.
- תבניות מבנה (Structural Patterns): תבניות אלו עוסקות בהרכבת אובייקטים, ומסבירות כיצד להרכיב אובייקטים ומחלקות למבנים גדולים יותר תוך שמירה על גמישות ויעילות המבנים הללו.
- תבניות התנהגות (Behavioral Patterns): תבניות אלו עוסקות באלגוריתמים ובהקצאת אחריויות בין אובייקטים. הן מתארות כיצד אובייקטים מתקשרים ומחלקים אחריות.
בואו נצלול לכל קטגוריה עם דוגמאות מעשיות.
תבניות יצירה: שליטה ביצירת אובייקטים
תבניות יצירה מספקות מנגנונים שונים ליצירת אובייקטים, המגבירים את הגמישות והשימוש החוזר בקוד קיים. הן עוזרות לנתק את המערכת מהאופן שבו האובייקטים שלה נוצרים, מורכבים ומיוצגים.
תבנית הסינגלטון (Singleton)
הרעיון: תבנית הסינגלטון מבטיחה שלמחלקה יהיה מופע אחד בלבד ומספקת נקודת גישה גלובלית ויחידה אליו. כל ניסיון ליצור מופע חדש יחזיר את המופע המקורי.
מקרי שימוש נפוצים: תבנית זו שימושית לניהול משאבים משותפים או מצב (state) משותף. דוגמאות כוללות מאגר חיבורי מסד נתונים יחיד, מנהל תצורה גלובלי, או שירות רישום לוגים (logging) שאמור להיות מאוחד בכל היישום.
מימוש ב-JavaScript: JavaScript מודרני, במיוחד עם מחלקות ES6, הופך את מימוש הסינגלטון לפשוט. אנו יכולים להשתמש במאפיין סטטי על המחלקה כדי להחזיק את המופע היחיד.
דוגמה: שירות לוגים בסינגלטון
class Logger { constructor() { if (Logger.instance) { return Logger.instance; } this.logs = []; Logger.instance = this; } log(message) { const timestamp = new Date().toISOString(); this.logs.push({ message, timestamp }); console.log(`${timestamp} - ${message}`); } getLogCount() { return this.logs.length; } } // The 'new' keyword is called, but the constructor logic ensures a single instance. const logger1 = new Logger(); const logger2 = new Logger(); console.log("Are loggers the same instance?", logger1 === logger2); // true logger1.log("First message from logger1."); logger2.log("Second message from logger2."); console.log("Total logs:", logger1.getLogCount()); // 2
יתרונות וחסרונות:
- יתרונות: מופע יחיד מובטח, מספק נקודת גישה גלובלית, וחוסך במשאבים על ידי הימנעות ממופעים מרובים של אובייקטים כבדים.
- חסרונות: יכולה להיחשב כאנטי-תבנית מכיוון שהיא מציגה מצב גלובלי, מה שמקשה על בדיקות יחידה. היא מצמדת בחוזקה את הקוד למופע הסינגלטון, ובכך מפרה את עיקרון הזרקת התלות (dependency injection).
תבנית הפקטורי (Factory)
הרעיון: תבנית הפקטורי מספקת ממשק ליצירת אובייקטים במחלקת-על, אך מאפשרת לתת-מחלקות לשנות את סוג האובייקטים שייווצרו. העיקר הוא להשתמש במתודה או מחלקה ייעודית "פקטורי" (factory) כדי ליצור אובייקטים מבלי לציין את המחלקות הקונקרטיות שלהם.
מקרי שימוש נפוצים: כאשר יש לכם מחלקה שאינה יכולה לצפות מראש את סוג האובייקטים שהיא צריכה ליצור, או כאשר אתם רוצים לספק למשתמשי הספרייה שלכם דרך ליצור אובייקטים מבלי שהם יצטרכו להכיר את פרטי המימוש הפנימיים. דוגמה נפוצה היא יצירת סוגים שונים של משתמשים (אדמין, חבר, אורח) על סמך פרמטר.
מימוש ב-JavaScript:
דוגמה: פקטורי משתמשים
class RegularUser { constructor(name) { this.name = name; this.role = 'Regular'; } viewDashboard() { console.log(`${this.name} is viewing the user dashboard.`); } } class AdminUser { constructor(name) { this.name = name; this.role = 'Admin'; } viewDashboard() { console.log(`${this.name} is viewing the admin dashboard with full privileges.`); } } class UserFactory { static createUser(type, name) { switch (type.toLowerCase()) { case 'admin': return new AdminUser(name); case 'regular': return new RegularUser(name); default: throw new Error('Invalid user type specified.'); } } } const admin = UserFactory.createUser('admin', 'Alice'); const regularUser = UserFactory.createUser('regular', 'Bob'); admin.viewDashboard(); // Alice is viewing the admin dashboard... regularUser.viewDashboard(); // Bob is viewing the user dashboard. console.log(admin.role); // Admin console.log(regularUser.role); // Regular
יתרונות וחסרונות:
- יתרונות: מקדמת צימוד רופף על ידי הפרדת קוד הלקוח מהמחלקות הקונקרטיות. הופכת את הקוד לגמיש יותר להרחבה, שכן הוספת סוגי מוצרים חדשים דורשת רק יצירת מחלקה חדשה ועדכון הפקטורי.
- חסרונות: עלולה להוביל להתרבות של מחלקות אם נדרשים סוגי מוצרים רבים ושונים, מה שהופך את בסיס הקוד למורכב יותר.
תבנית הפרוטוטייפ (Prototype)
הרעיון: תבנית הפרוטוטייפ עוסקת ביצירת אובייקטים חדשים על ידי העתקת אובייקט קיים, המכונה "הפרוטוטייפ". במקום לבנות אובייקט מאפס, אתם יוצרים שיבוט (clone) של אובייקט שהוגדר מראש. זהו עיקרון בסיסי לאופן שבו JavaScript עצמה עובדת באמצעות ירושה פרוטוטופיאלית.
מקרי שימוש נפוצים: תבנית זו שימושית כאשר העלות של יצירת אובייקט יקרה או מורכבת יותר מהעתקת אובייקט קיים. היא משמשת גם ליצירת אובייקטים שסוגם מצוין בזמן ריצה.
מימוש ב-JavaScript: ל-JavaScript יש תמיכה מובנית בתבנית זו באמצעות `Object.create()`.
דוגמה: פרוטוטייפ של רכב שניתן לשבט
const vehiclePrototype = { init: function(model) { this.model = model; }, getModel: function() { return `The model of this vehicle is ${this.model}`; } }; // Create a new car object based on the vehicle prototype const car = Object.create(vehiclePrototype); car.init('Ford Mustang'); console.log(car.getModel()); // The model of this vehicle is Ford Mustang // Create another object, a truck const truck = Object.create(vehiclePrototype); truck.init('Tesla Cybertruck'); console.log(truck.getModel()); // The model of this vehicle is Tesla Cybertruck
יתרונות וחסרונות:
- יתרונות: יכולה לספק שיפור ביצועים משמעותי ביצירת אובייקטים מורכבים. מאפשרת להוסיף או להסיר מאפיינים מאובייקטים בזמן ריצה.
- חסרונות: יצירת שיבוטים של אובייקטים עם הפניות מעגליות (circular references) יכולה להיות מסובכת. ייתכן שיהיה צורך בהעתקה עמוקה (deep copy), שעלולה להיות מורכבת למימוש נכון.
תבניות מבנה: הרכבת קוד בצורה חכמה
תבניות מבנה עוסקות באופן שבו ניתן לשלב אובייקטים ומחלקות ליצירת מבנים גדולים ומורכבים יותר. הן מתמקדות בפישוט המבנה ובזיהוי יחסים.
תבנית המתאם (Adapter)
הרעיון: תבנית המתאם פועלת כגשר בין שני ממשקים שאינם תואמים. היא כוללת מחלקה אחת (המתאם) המגשרת בין פונקציונליות של ממשקים עצמאיים או לא תואמים. חשבו על זה כמו מתאם חשמל שמאפשר לכם לחבר את המכשיר שלכם לשקע חשמלי זר.
מקרי שימוש נפוצים: שילוב ספריית צד-שלישי חדשה עם יישום קיים המצפה ל-API שונה, או גרימה לקוד ישן (legacy) לעבוד עם מערכת מודרנית מבלי לשכתב את הקוד הישן.
מימוש ב-JavaScript:
דוגמה: התאמת API חדש לממשק ישן
// The old, existing interface our application uses class OldCalculator { operation(term1, term2, operation) { switch (operation) { case 'add': return term1 + term2; case 'sub': return term1 - term2; default: return NaN; } } } // The new, shiny library with a different interface class NewCalculator { add(term1, term2) { return term1 + term2; } subtract(term1, term2) { return term1 - term2; } } // The Adapter class class CalculatorAdapter { constructor() { this.calculator = new NewCalculator(); } operation(term1, term2, operation) { switch (operation) { case 'add': // Adapting the call to the new interface return this.calculator.add(term1, term2); case 'sub': return this.calculator.subtract(term1, term2); default: return NaN; } } } // Client code can now use the adapter as if it were the old calculator const oldCalc = new OldCalculator(); console.log("Old calculator result:", oldCalc.operation(10, 5, 'add')); // 15 const adaptedCalc = new CalculatorAdapter(); console.log("Adapted calculator result:", adaptedCalc.operation(10, 5, 'add')); // 15
יתרונות וחסרונות:
- יתרונות: מפרידה את הלקוח מהמימוש של ממשק היעד, ומאפשרת שימוש במימושים שונים באופן חליפי. משפרת את השימוש החוזר בקוד.
- חסרונות: יכולה להוסיף שכבת מורכבות נוספת לקוד.
תבנית המעטר (Decorator)
הרעיון: תבנית המעטר מאפשרת לצרף באופן דינמי התנהגויות או אחריויות חדשות לאובייקט מבלי לשנות את הקוד המקורי שלו. זה מושג על ידי עטיפת האובייקט המקורי באובייקט "מעטר" מיוחד המכיל את הפונקציונליות החדשה.
מקרי שימוש נפוצים: הוספת תכונות לרכיב UI, הרחבת אובייקט משתמש עם הרשאות, או הוספת התנהגות רישום לוגים/מטמון (caching) לשירות. זוהי חלופה גמישה ליצירת תת-מחלקות.
מימוש ב-JavaScript: פונקציות הן אזרחים ממדרגה ראשונה ב-JavaScript, מה שהופך את מימוש המעטרים לקל.
דוגמה: עיטור הזמנת קפה
// The base component class SimpleCoffee { getCost() { return 10; } getDescription() { return 'Simple coffee'; } } // Decorator 1: Milk function MilkDecorator(coffee) { const originalCost = coffee.getCost(); const originalDescription = coffee.getDescription(); coffee.getCost = function() { return originalCost + 2; }; coffee.getDescription = function() { return `${originalDescription}, with milk`; }; return coffee; } // Decorator 2: Sugar function SugarDecorator(coffee) { const originalCost = coffee.getCost(); const originalDescription = coffee.getDescription(); coffee.getCost = function() { return originalCost + 1; }; coffee.getDescription = function() { return `${originalDescription}, with sugar`; }; return coffee; } // Let's create and decorate a coffee let myCoffee = new SimpleCoffee(); console.log(myCoffee.getCost(), myCoffee.getDescription()); // 10, Simple coffee myCoffee = MilkDecorator(myCoffee); console.log(myCoffee.getCost(), myCoffee.getDescription()); // 12, Simple coffee, with milk myCoffee = SugarDecorator(myCoffee); console.log(myCoffee.getCost(), myCoffee.getDescription()); // 13, Simple coffee, with milk, with sugar
יתרונות וחסרונות:
- יתרונות: גמישות רבה להוסיף אחריויות לאובייקטים בזמן ריצה. נמנעת ממחלקות עמוסות בתכונות במעלה ההיררכיה.
- חסרונות: יכולה להוביל למספר רב של אובייקטים קטנים. סדר המעטרים יכול להיות משנה, מה שעשוי לא להיות ברור ללקוחות.
תבנית החזית (Facade)
הרעיון: תבנית החזית מספקת ממשק פשוט וברמה גבוהה לתת-מערכת מורכבת של מחלקות, ספריות או ממשקי API. היא מסתירה את המורכבות הבסיסית והופכת את תת-המערכת לקלה יותר לשימוש.
מקרי שימוש נפוצים: יצירת API פשוט למערך פעולות מורכב, כגון תהליך תשלום (checkout) במסחר אלקטרוני הכולל תתי-מערכות של מלאי, תשלום ומשלוח. דוגמה נוספת היא מתודה יחידה להפעלת יישום ווב, אשר באופן פנימי מגדירה את השרת, מסד הנתונים וה-middleware.
מימוש ב-JavaScript:
דוגמה: חזית לבקשת משכנתא
// Complex Subsystems class BankService { verify(name, amount) { console.log(`Verifying sufficient funds for ${name} for amount ${amount}`); return amount < 100000; } } class CreditHistoryService { get(name) { console.log(`Checking credit history for ${name}`); // Simulate a good credit score return true; } } class BackgroundCheckService { run(name) { console.log(`Running background check for ${name}`); return true; } } // The Facade class MortgageFacade { constructor() { this.bank = new BankService(); this.credit = new CreditHistoryService(); this.background = new BackgroundCheckService(); } applyFor(name, amount) { console.log(`--- Applying for mortgage for ${name} ---`); const isEligible = this.bank.verify(name, amount) && this.credit.get(name) && this.background.run(name); const result = isEligible ? 'Approved' : 'Rejected'; console.log(`--- Application result for ${name}: ${result} ---\n`); return result; } } // Client code interacts with the simple Facade const mortgage = new MortgageFacade(); mortgage.applyFor('John Smith', 75000); // Approved mortgage.applyFor('Jane Doe', 150000); // Rejected
יתרונות וחסרונות:
- יתרונות: מנתקת את הלקוח מהפעולה הפנימית המורכבת של תת-מערכת, ומשפרת את הקריאות והתחזוקתיות.
- חסרונות: החזית עלולה להפוך ל"אובייקט אלוהים" (god object) המצומד לכל המחלקות של תת-מערכת. היא אינה מונעת מלקוחות לגשת ישירות למחלקות תת-המערכת אם הם זקוקים לגמישות רבה יותר.
תבניות התנהגות: תזמור תקשורת בין אובייקטים
תבניות התנהגות עוסקות כולן באופן שבו אובייקטים מתקשרים זה עם זה, תוך התמקדות בהקצאת אחריויות וניהול אינטראקציות ביעילות.
תבנית הצופה (Observer)
הרעיון: תבנית הצופה מגדירה תלות של אחד-לרבים בין אובייקטים. כאשר אובייקט אחד (ה-"נושא" או "observable") משנה את מצבו, כל האובייקטים התלויים בו (ה-"צופים" או "observers") מקבלים הודעה ומתעדכנים באופן אוטומטי.
מקרי שימוש נפוצים: תבנית זו היא הבסיס לתכנות מונחה-אירועים (event-driven). היא נמצאת בשימוש נרחב בפיתוח UI (מאזיני אירועים ב-DOM), ספריות ניהול מצב (כמו Redux או Vuex), ומערכות העברת הודעות.
מימוש ב-JavaScript:
דוגמה: סוכנות ידיעות ומנויים
// The Subject (Observable) class NewsAgency { constructor() { this.subscribers = []; } subscribe(subscriber) { this.subscribers.push(subscriber); console.log(`${subscriber.name} has subscribed.`); } unsubscribe(subscriber) { this.subscribers = this.subscribers.filter(sub => sub !== subscriber); console.log(`${subscriber.name} has unsubscribed.`); } notify(news) { console.log(`--- NEWS AGENCY: Broadcasting news: "${news}" ---`); this.subscribers.forEach(subscriber => subscriber.update(news)); } } // The Observer class Subscriber { constructor(name) { this.name = name; } update(news) { console.log(`${this.name} received the latest news: "${news}"`); } } const agency = new NewsAgency(); const sub1 = new Subscriber('Reader A'); const sub2 = new Subscriber('Reader B'); const sub3 = new Subscriber('Reader C'); agency.subscribe(sub1); agency.subscribe(sub2); agency.notify('Global markets are up!'); agency.subscribe(sub3); agency.unsubscribe(sub2); agency.notify('New tech breakthrough announced!');
יתרונות וחסרונות:
- יתרונות: מקדמת צימוד רופף בין הנושא לצופים שלו. הנושא אינו צריך לדעת דבר על הצופים שלו מלבד העובדה שהם מממשים את ממשק הצופה. תומכת בתקשורת בסגנון שידור (broadcast).
- חסרונות: הצופים מקבלים הודעות בסדר לא צפוי. עלול להוביל לבעיות ביצועים אם ישנם צופים רבים או אם לוגיקת העדכון מורכבת.
תבנית האסטרטגיה (Strategy)
הרעיון: תבנית האסטרטגיה מגדירה משפחה של אלגוריתמים הניתנים להחלפה ומכניסה כל אחד מהם למחלקה משלו. זה מאפשר לבחור ולהחליף את האלגוריתם בזמן ריצה, באופן בלתי תלוי בלקוח המשתמש בו.
מקרי שימוש נפוצים: מימוש אלגוריתמי מיון שונים, כללי אימות (validation), או שיטות לחישוב עלות משלוח באתר מסחר אלקטרוני (למשל, מחיר אחיד, לפי משקל, לפי יעד).
מימוש ב-JavaScript:
דוגמה: אסטרטגיה לחישוב עלות משלוח
// The Context class Shipping { constructor() { this.company = null; } setStrategy(company) { this.company = company; console.log(`Shipping strategy set to: ${company.constructor.name}`); } calculate(pkg) { if (!this.company) { throw new Error('Shipping strategy has not been set.'); } return this.company.calculate(pkg); } } // The Strategies class FedExStrategy { calculate(pkg) { // Complex calculation based on weight, etc. const cost = pkg.weight * 2.5 + 5; console.log(`FedEx cost for package of ${pkg.weight}kg is $${cost}`); return cost; } } class UPSStrategy { calculate(pkg) { const cost = pkg.weight * 2.1 + 4; console.log(`UPS cost for package of ${pkg.weight}kg is $${cost}`); return cost; } } class PostalServiceStrategy { calculate(pkg) { const cost = pkg.weight * 1.8; console.log(`Postal Service cost for package of ${pkg.weight}kg is $${cost}`); return cost; } } const shipping = new Shipping(); const packageA = { from: 'New York', to: 'London', weight: 5 }; shipping.setStrategy(new FedExStrategy()); shipping.calculate(packageA); shipping.setStrategy(new UPSStrategy()); shipping.calculate(packageA); shipping.setStrategy(new PostalServiceStrategy()); shipping.calculate(packageA);
יתרונות וחסרונות:
- יתרונות: מספקת חלופה נקייה להצהרת `if/else` או `switch` מורכבת. מכניסה אלגוריתמים לקפסולות, מה שהופך אותם לקלים יותר לבדיקה ותחזוקה.
- חסרונות: יכולה להגדיל את מספר האובייקטים ביישום. על הלקוחות להיות מודעים לאסטרטגיות השונות כדי לבחור את הנכונה.
תבניות מודרניות ושיקולים ארכיטקטוניים
בעוד שתבניות עיצוב קלאסיות הן נצחיות, האקוסיסטם של JavaScript התפתח, והוליד פרשנויות מודרניות ותבניות ארכיטקטוניות רחבות היקף שהן חיוניות למפתחים של ימינו.
תבנית המודול (Module)
תבנית המודול הייתה אחת התבניות הנפוצות ביותר ב-JavaScript שלפני ES6 ליצירת טווחי הגדרה (scopes) פרטיים וציבוריים. היא משתמשת בסגור (closure) כדי לכמוס מצב והתנהגות. כיום, תבנית זו הוחלפה במידה רבה על ידי מודולי ES6 המקוריים (`import`/`export`), המספקים מערכת מודולים סטנדרטית מבוססת קבצים. הבנת מודולי ES6 היא יסודית לכל מפתח JavaScript מודרני, שכן הם הסטנדרט לארגון קוד ביישומי פרונט-אנד ובק-אנד כאחד.
תבניות ארכיטקטוניות (MVC, MVVM)
חשוב להבחין בין תבניות עיצוב לבין תבניות ארכיטקטוניות. בעוד שתבניות עיצוב פותרות בעיות ספציפיות ומקומיות, תבניות ארכיטקטוניות מספקות מבנה ברמה גבוהה ליישום שלם.
- MVC (Model-View-Controller): תבנית המפרידה יישום לשלושה רכיבים המקושרים זה לזה: המודל (נתונים ולוגיקה עסקית), התצוגה (ה-UI), והבקר (מטפל בקלט משתמש ומעדכן את המודל/תצוגה). מסגרות כמו Ruby on Rails וגרסאות ישנות של Angular הפכו אותה לפופולרית.
- MVVM (Model-View-ViewModel): דומה ל-MVC, אך כוללת ViewModel שפועל כ"מקשר" (binder) בין המודל לתצוגה. ה-ViewModel חושף נתונים ופקודות, והתצוגה מתעדכנת אוטומטית הודות לקשירת נתונים (data-binding). תבנית זו היא מרכזית במסגרות מודרניות כמו Vue.js ומשפיעה על הארכיטקטורה מבוססת הרכיבים של React.
כאשר עובדים עם מסגרות כמו React, Vue, או Angular, אתם משתמשים באופן אינהרנטי בתבניות ארכיטקטוניות אלו, לעתים קרובות בשילוב עם תבניות עיצוב קטנות יותר (כמו תבנית הצופה לניהול מצב) כדי לבנות יישומים חסונים.
סיכום: שימוש מושכל בתבניות
תבניות עיצוב ב-JavaScript אינן כללים נוקשים אלא כלים רבי עוצמה בארסנל של המפתח. הן מייצגות את החוכמה הקולקטיבית של קהילת הנדסת התוכנה, ומציעות פתרונות אלגנטיים לבעיות נפוצות.
המפתח לשליטה בהן אינו לשנן כל תבנית, אלא להבין את הבעיה שכל אחת מהן פותרת. כאשר אתם נתקלים באתגר בקוד שלכם — בין אם זה צימוד הדוק, יצירת אובייקטים מורכבת, או אלגוריתמים לא גמישים — אתם יכולים אז לפנות לתבנית המתאימה כפתרון מוגדר היטב.
העצה האחרונה שלנו היא זו: התחילו בכתיבת הקוד הפשוט ביותר שעובד. ככל שהיישום שלכם מתפתח, בצעו ריפקטורינג (refactor) לקוד שלכם לכיוון התבניות הללו במקומות שהן מתאימות באופן טבעי. אל תכפו תבנית היכן שהיא אינה נחוצה. על ידי יישומן בשיקול דעת, תכתבו קוד שהוא לא רק פונקציונלי, אלא גם נקי, סקיילבילי, ונעים לתחזוקה לשנים הבאות.