תבניות עיצוב בג'אווה סקריפט – Observer Pattern

Published on
Authors
  • avatar
    Name
    Yonatan Snir

תבנית הצופה – Observer Pattern היא תבנית שעוזרת לנו להגדיר מערכת יחסים בין אובייקט אחד שנקרא "הנושא" לבין כמה אובייקטים אחרים שתלויים בו ונקראים "צופים". את אובייקט הנושא לא מעניין מה קורה עם האובייקטים שצופים בו והוא לא תלוי בהם, אך הם כן תלויים בו ומחכים שיקרא להם באמצעות אחת הפונקציות שלו.

זאת התבנית האהובה עליי. תבנית הצופה משמשת בדר"כ לטיפול באירועים כמו לחיצה על כפתור מסויים – שגורם להפעלת אלמנטים אחרים באפליקציה. זאת אחת התבניות הנפוצות ביותר בממשקי משתמש ובספריות כמו jQuery, וריאקט.

אז איך מיישמים אותה בצורה הפשוטה ביותר? דבר ראשון נצטרך לכתוב את אובייקט הנושא שלנו ובתוכו מערך של כל הצופים שלו:

function Subject(){
    this.observers = [];
}

אחרי שכתבנו את אובייקט הנושא, נרצה להוסיף לו כמה מתודות.

Subject.prototype = {
   subscribe: function(fn){
       this.observers.push(fn)
   },
   unsubscribe: function(fnToRemove){
       this.observers = this.observers.filter(fn => {
          if(fn != fnToRemove){
              return fn;
           }
       })
   },
   fire: function(){
      this.observers.forEach(fn => fn());
    }
}

הוספנו שלוש מתודות (פונקציות) לאובייקט הנושא שלנו. הראשונה subscribe מוסיפה פונקציה למערך הצופים. השניה unsubscribe מסירה פונקציה ממערך הצופים. האחרונה fire קוראת לכל האובייקטים שצופים באובייקט שלנו.

וזהו בערך. זאת התבנית שלנו והיא מוכנה לשימוש. בואו נבדוק אותה עכשיו! ניצור את אובייקט הנושא שלנו, ונכתוב עוד 2 אובייקטים ש"צופים" בו.

const subject = new Subject(); // We create the subject object.

function  Observer1(){
    console.log('Observer 1 has called!')
}
function  Observer2(){
    console.log('Observer 2 has called!')
}

יש לנו את אובייקט הנושא, ויש לנו שני אובייקטים (פונקציות בתכלס) שאמורות לצפות בו ולהגיב לשינויים בו. אבל… עדיין לא הוספנו אותן למערך הצופים שלנו זוכרים? בואו נרשום את הפונקציות האלו בתור "צופות" באמצעות המתודה subscribe שכתבנו

subject.subscribe(Observer1);
subject.subscribe(Observer2);

עכשיו הפונקציות האלו רשומות אצל האובייקט הנושא, ונוכל להעיר אותן בכל פעם שנעשה שינוי באובייקט הזה באמצעות המתודה fire שכתבנו:

subject.fire();

אם נריץ את הקוד, הפלט ב-console יהיה:

Observer 1 has called!
Observer 2 has called!

אם נרצה להסיר צופה מהרשימה:

subject.unsubscribe(Observer1);

פשוט וקל. עם ES6:

class Subject {
    constructor(){
        this.observers = [];
   };

    subscribe(fn){
       this.observers.push(fn)
    };

    unsubscribe(fnToRemove){
       this.observers = this.observers.filter(fn => {
          if(fn != fnToRemove){
              return fn;
           }
       })
   };

   fire(){
      this.observers.forEach(fn => fn());
    }
}