תבנית עיצוב המתבצעת ב-Java

תבנית המעקב היא אחת מתבניות העיצוב ההתנהגותיות. תבנית המעקב מועילה כאשר אתה מעוניין במצב של אובייקט ורוצה לקבל התראה בכל פעם שיש שינוי. בתבנית המעקב, האובייקט ששומר על מצב של אובייקט אחר נקרא עוקב והאובייקט שנשקף עליו נקרא נושא.

תבנית העיצוב של מעקב

לפי GoF, מטרת תבנית העיצוב של מעקב היא;

להגדיר תלות אחת למון בין אובייקטים כך שכאשר אובייקט משנה מצב, כל התלויים שלו מתריעים ומתעדכנים אוטומטית.

נושא מכיל רשימת צופים להודיע על כל שינוי במצבו, לכן עליו לספק שיטות באמצעותן צופים יכולים להירשם ולהיסר. הנושא מכיל גם שיטה להודיע לכל הצופים על כל שינוי, ואו שהוא יכול לשלוח את העדכון במהלך הודעת הצופים או שהוא יכול לספק שיטה נוספת לקבלת העדכון. לצופה צריך להיות שיטה להגדרת האובייקט שהוא מתכוון לצפות בו ושיטה נוספת שתשמש על ידי הנושא להודיע להם על כל עדכונים. Java מספקת פלטפורמה מובנית ליישום של תבנית ה-Observer דרך מחלקת java.util.Observable וממשק java.util.Observer. אולם השימוש בה לא נרחב מאוד מכיוון שהמימוש הוא באמת פשוט מאוד וברוב המקרים אנו לא רוצים להסתיים עם הרחבה של מחלקה רק ליישום של תבנית ה-Observer מאחר ו-Java לא מספקת ירושה מרובה במחלקות. שירות ההודעות של Java (JMS) משתמש ב-תבנית ה-Observer במקביל עם תבנית ה-מתווך כדי לאפשר ליישומים להירשם ולפרסם נתונים ליישומים אחרים. מסגרות Model-View-Controller (MVC) גם משתמשות בתבנית ה-Observer, כאשר המודל הוא הנושא והתצוגות הן צופים שיכולים להירשם כדי לקבל הודעה על כל שינוי במודל.

דוגמה לתבנית ה-Observer ב-Java

עבור דוגמת תוכנית ה-Java שלנו שמשתמשת בתבנית ה-Observable, נטמיע נושא פשוט ומתעדים יכולים להירשם לנושא זה. בכל פעם שיתקבל הודעה חדשה לנושא, כל המתעדים הרשומים יודעים ויכולים לצרוך את ההודעה. בהתבסס על דרישות הנושא, הנה ממשק הנושא הבסיסי שמגדיר את השיטות החוזיות שיש ליישם על ידי כל נושא מוחלט.

package com.journaldev.design.observer;

public interface Subject {

	 // שיטות להרשמה והרשמה חוזרת של המתעדים 
	public void register(Observer obj);
	public void unregister(Observer obj);
	
	 // שיטה להודיע למתעדים על שינוי 
	public void notifyObservers();
	
	 // שיטה לקבל עדכונים מהנושא 
	public Object getUpdate(Observer obj);
	
}

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

package com.journaldev.design.observer;

public interface Observer {
	
	 // שיטה לעדכון המתעד, המשמש על ידי הנושא 
	public void update();
	
	 // הצמד עם הנושא לצפות 
	public void setSubject(Subject sub);
}

כעת החוזה שלנו מוכן, בואו נמשיך עם היישום הממשי של הנושא שלנו.

package com.journaldev.design.observer;

import java.util.ArrayList;
import java.util.List;

public class MyTopic implements Subject {

	private List observers;
	private String message;
	private boolean changed;
	private final Object MUTEX= new Object();
	
	public MyTopic(){
		this.observers=new ArrayList<>();
	}
	@Override
	public void register(Observer obj) {
		if(obj == null) throw new NullPointerException("Null Observer");
		synchronized (MUTEX) {
		if(!observers.contains(obj)) observers.add(obj);
		}
	}

	@Override
	public void unregister(Observer obj) {
		synchronized (MUTEX) {
		observers.remove(obj);
		}
	}

	@Override
	public void notifyObservers() {
		List observersLocal = null;
		 // סנכרון משמש כדי לוודא כי כל מתעד הרשום לאחר שנמצאת הודעה לא יודע 
		synchronized (MUTEX) {
			if (!changed)
				return;
			observersLocal = new ArrayList<>(this.observers);
			this.changed=false;
		}
		for (Observer obj : observersLocal) {
			obj.update();
		}

	}

	@Override
	public Object getUpdate(Observer obj) {
		return this.message;
	}
	
	 // שיטה לפרסום הודעה לנושא
	public void postMessage(String msg){
		System.out.println("Message Posted to Topic:"+msg);
		this.message=msg;
		this.changed=true;
		notifyObservers();
	}

}

המימוש של שיטה לרשום ולבטל רישום של צופה הוא פשוט מאוד, השיטה הנוספת היא postMessage() שתשמש על ידי יישום הלקוח לפרסום הודעת מחרוזת לנושא. שימו לב למשתנה הבוליאני המשמש לעקוב אחר שינויים במצב הנושא ומשמש בהודעה לצופים. מדובר במשתנה הדרוש כדי שאם אין עדכון ומישהו קורא לשיטה notifyObservers(), אז היא לא תשלח התראות שקריות לצופים. שימו לב גם לשימוש ב- synchronization בשיטה notifyObservers() כדי לוודא שההודעה נשלחת רק לצופים שנרשמו לפני שההודעה פורסמה לנושא. הנה המימוש של צופים שישמשו את הנושא.

package com.journaldev.design.observer;

public class MyTopicSubscriber implements Observer {
	
	private String name;
	private Subject topic;
	
	public MyTopicSubscriber(String nm){
		this.name=nm;
	}
	@Override
	public void update() {
		String msg = (String) topic.getUpdate(this);
		if(msg == null){
			System.out.println(name+":: No new message");
		}else
		System.out.println(name+":: Consuming message::"+msg);
	}

	@Override
	public void setSubject(Subject sub) {
		this.topic=sub;
	}

}

שימו לב למימוש של שיטת update() שבה קוראים לשיטת getUpdate() של הנושא כדי לקבל את ההודעה לצריבה. היינו יכולים להימנע מהשיחה הזו על ידי העברת ההודעה כארגומנט לשיטה update(). הנה תוכנית בדיקה פשוטה לצריבת המימוש שלנו לנושא.

package com.journaldev.design.observer;

public class ObserverPatternTest {

	public static void main(String[] args) {
		//יצירת נושא
		MyTopic topic = new MyTopic();
		
		//יצירת צופים
		Observer obj1 = new MyTopicSubscriber("Obj1");
		Observer obj2 = new MyTopicSubscriber("Obj2");
		Observer obj3 = new MyTopicSubscriber("Obj3");
		
		//רישום צופים לנושא
		topic.register(obj1);
		topic.register(obj2);
		topic.register(obj3);
		
		//צירוף צופים לנושא
		obj1.setSubject(topic);
		obj2.setSubject(topic);
		obj3.setSubject(topic);
		
		//בדיקה אם יש עדכון זמין
		obj1.update();
		
		//שליחת הודעה לנושא
		topic.postMessage("New Message");
	}

}

כאשר אנו מריצים את התוכנית לעיל, אנו מקבלים את הפלט הבא.

Obj1:: No new message
Message Posted to Topic:New Message
Obj1:: Consuming message::New Message
Obj2:: Consuming message::New Message
Obj3:: Consuming message::New Message

תרשים כיתת תבנית העקרונות בעזרת דגם המפקח של Java

דגם העיצוב המפקח נקרא גם דגם פרסום-הרשמה. חלק מהמימושים שלו הם;

  • java.util.EventListener ב-Swing
  • javax.servlet.http.HttpSessionBindingListener
  • javax.servlet.http.HttpSessionAttributeListener

זהו הכל לדגם עיצוב המפקח ב-Java, אני מקווה שאהבת. שתף את אהבתך בתגובות ובשיתוף עם אחרים.

Source:
https://www.digitalocean.com/community/tutorials/observer-design-pattern-in-java