تنفيذ نمط المراقب البسيط
انسخ رمز الكود كما يلي:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* يتم استخدام عمليات الاسترجاعات في وضع المراقب:
* أ. يقوم المراقب بتسجيل نفسه في قائمة المستمعين للمراقب، وتوفر فئة المراقب نفسها وظيفة رد الاتصال
* ب. يحتفظ القابل للملاحظة (القابل للملاحظة أو الموضوع) بقائمة من المراقبين ويمكنه تسجيل المراقبين وإلغاء تسجيلهم
* ج. بمجرد تغيير حالة المراقب، يمكنه استدعاء notifyObservers(). ستجتاز هذه الطريقة قائمة المراقبين وتستدعيها واحدًا تلو الآخر.
وظيفة رد الاتصال التي يوفرها المراقب
* @المؤلف سوف
*
*/
الطبقة العامة SimpleObserverPattern {
public static void main(String[] args) {
SimpleObserverPattern sop = new SimpleObserverPattern();
List<IObserver> Observers = new ArrayList<IObserver> ();
IObserver ObserverA = sop.new Observer("ObserverA");
IObserver ObserverB = sop.new Observer("ObserverB");
Observers.add(observerA);
Observers.add(observerB);
IObservable observable = sop.new Observable(observers);
observable.registerObserver(sop.new Observer("ObserverC"));
observable.changeState();
يمكن ملاحظتها. إغلاق ()؛
}
// يُطلق على الشخص الذي تتم مراقبته اسم الموضوع في بعض الأماكن.
واجهة يمكن ملاحظتها {
void RegisterObserver(IObserver Observer);
void unregisterObserver(IObserver Observer);
إعلام باطلة () ؛
سلسلة getState();
تغيير الحالة باطلة () ؛
إغلاق باطل () ؛
}
فئة يمكن ملاحظتها تنفذ IObservable {
سلسلة نهائية ثابتة خاصة جديدة = "جديد"؛
سلسلة نهائية ثابتة خاصة تم تغييرها = "تم التغيير"؛
سلسلة نهائية ثابتة خاصة CLOSED = "مغلق"؛
حالة سلسلة خاصة؛
قائمة خاصة<IObserver> للمراقبين؛
عامة يمكن ملاحظتها () {
هذا(خالي);
}
عامة يمكن ملاحظتها (قائمة<IObserver> المراقبين) {
إذا (المراقبون == فارغة) {
Observers = new ArrayList<IObserver> ();
}
this.observers = Collections.synchronizedList(observers);
this.state = NEW;
}
@تجاوز
تسجيل الفراغ العام (مراقب IObserver) {
Observers.add(observer);
}
@تجاوز
الفراغ العام unregisterObserver(IObserver المراقب) {
المراقبين.إزالة(المراقب);
}
@تجاوز
إعلام الفراغ العام () {
Iterator<IObserver> iter = Observers.iterator();
بينما(iter.hasNext()) {
iter.next().update(this);
}
}
@تجاوز
سلسلة عامة getState () {
حالة العودة؛
}
@تجاوز
تغيير حالة الفراغ العام () {
this.state = CHANGED;
notifyObservers();
}
@تجاوز
إغلاق الفراغ العام () {
this.state = مغلق؛
notifyObservers();
}
}
واجهة IObserver {
تحديث باطل(IObservable observalbe);
}
مراقب الطبقة يطبق IObserver {
اسم سلسلة خاصة؛
المراقب العام (اسم السلسلة) {
this.name = name;
}
@تجاوز
تحديث باطل عام (IObservable observalbe) {
System.out.println(
String.format("%s يتلقى تغيير الملاحظة، حالة الملاحظة الحالية هي %s"،
الاسم، observalbe.getState()));
}
}
}
يستخدم التنفيذ أعلاه الكائن الذي تمت ملاحظته مباشرة كمعلمة لوظيفة رد الاتصال، وهذا غير أنيق للغاية وقد يعمل في سيناريوهات بسيطة.
ولكن في الواقع، في أكثر الحالات، يكون لدى الشخص المرصود العديد من الأحداث أو الحالات، وقد يكون كل مراقب مهتمًا بأحداث أو حالات مختلفة، أو لغرض إخفاء المعلومات، فلا نريد أن يتمكن كل مراقب من الوصول إلى جميع الحالات داخل ما يمكن ملاحظته.
بهذه الطريقة، واصلت تطوير الكود إلى الإصدار التالي. لاحظ أنني لم أفكر في مشكلات التزامن بالتفصيل هنا.
انسخ رمز الكود كما يلي:
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
الطبقة العامة MultiEventObserverPattern {
public static void main(String[] args) {
MultiEventObserverPattern meop = new MultiEventObserverPattern();
IObservable observable = meop.new Observable();
IObserver ObserverA = meop.new Observer("ObserverA");
IObserver ObserverB = meop.new Observer("ObserverB");
// تسجيل الأحداث ذات الاهتمام
observable.registerObserver(observable.getEventA(), ObserverA);
observable.registerObserver(observable.getEventB(), ObserverB);
// تغيير الحالة المرصودة
observable.changeStateA();
observable.changeStateB();
}
واجهة الحدث {
حدث باطل () ؛
سلسلة getState();
}
فئة EventA تنفذ IEvent {
السلسلة النهائية الثابتة الخاصة INITIALIZED = "تمت التهيئة"؛
سلسلة نهائية ثابتة خاصة معلقة = "معلقة"؛
حالة سلسلة خاصة؛
الحدث العام () {
this.state = INITIALIZED;
}
@تجاوز
حدث الفراغ العام () {
System.out.println("تغيير الحدث");
this.state = PENDING;
}
@تجاوز
سلسلة عامة إلى سلسلة () {
إرجاع "EventA" ؛
}
@تجاوز
سلسلة عامة getState () {
حالة العودة؛
}
}
فئة EventB تنفذ IEvent {
سلسلة نهائية ثابتة خاصة جديدة = "جديد"؛
سلسلة نهائية ثابتة خاصة IDLE = "خامل"؛
حالة سلسلة خاصة؛
الحدث العام () {
this.state = NEW;
}
@تجاوز
حدث الفراغ العام () {
System.out.println("تغيير الحدثB");
this.state = IDLE;
}
@تجاوز
سلسلة عامة إلى سلسلة () {
إرجاع "الحدثB"؛
}
@تجاوز
سلسلة عامة getState () {
حالة العودة
}
}
// يمكن ملاحظته، ويسمى أحيانًا الموضوع
واجهة يمكن ملاحظتها {
void RegisterObserver(IEvent events, IObserver Observer);
void unregisterObserver(IEvent events, IObserver Observer);
// إعلام المراقبين بوقوع حدث ما
void notifyObservers(IEvent events);
تغيير حالة الفراغA();
تغيير حالة الفراغ () ؛
IEvent getEventA();
IEvent getEventB();
}
فئة يمكن ملاحظتها تنفذ IObservable {
حدث IEvent الخاص أ؛
حدث IEvent الخاص B؛
خاص Hashtable<IEvent, Set<IObserver>> eventsObserverMapping;
عامة يمكن ملاحظتها () {
هذا(خالي);
}
// إذا لم يتم تعديل بعض Set<IObserver> التي تم تمريرها بواسطة EvenObserverMapping بشكل متزامن، فلا يمكنك فعل أي شيء حيال ذلك.
public Observable(Hashtable<IEvent, Set<IObserver>> eventsObserverMapping) {
إذا (eventObserverMapping == فارغة) {
eventsObserverMapping = new Hashtable<IEvent, Set<IObserver>> ();
}
this.eventObserverMapping = new Hashtable<IEvent, Set<IObserver>> ();
this.eventA = new EventA();
this.eventB = new EventB();
}
@تجاوز
تسجيل الفراغ العام (حدث IEvent، مراقب IObserver) {
Set<IObserver> Observers = eventsObserverMapping.get(event);
إذا (المراقبون == فارغة) {
المراقبون = Collections.synchronizedSet(new HashSet<IObserver> ());
Observers.add(observer);
EventObserverMapping.put(event, Observers);
}
آخر {
Observers.add(observer);
}
}
@تجاوز
الفراغ العام unregisterObserver (حدث IEvent، مراقب IObserver) {
Set<IObserver> Observers = eventsObserverMapping.get(event);
إذا (المراقبون!= فارغة) {
المراقبين.إزالة(المراقب);
}
}
@تجاوز
إعلام الفراغ العام (حدث IEvent) {
Set<IObserver> Observers = eventsObserverMapping.get(event);
إذا (المراقبون!= فارغة && المراقبون.حجم() > 0) {
Iterator<IObserver> iter = Observers.iterator();
بينما(iter.hasNext()) {
iter.next().update(event);
}
}
}
@تجاوز
تغيير الفراغ العام () {
// سيؤدي تغيير الحالة A إلى تشغيل الحدث A
EventA.eventChange();
notifyObservers(eventA);
}
@تجاوز
تغيير الفراغ العام () {
// سيؤدي تغيير الحالة B إلى تشغيل الحدث B
eventsB.eventChange();
notifyObservers(eventB);
}
@تجاوز
IEvent العامة getEventA() {
عودة الحدث أ؛
}
@تجاوز
IEvent العامة getEventB() {
عودة الحدث ب؛
}
}
واجهة IObserver {
تحديث باطل (حدث IEvent) ؛
}
مراقب الطبقة ينفذ IObserver {
اسم سلسلة خاصة؛
المراقب العام (اسم السلسلة) {
this.name = name;
}
@تجاوز
تحديث باطل عام (حدث IEvent) {
System.out.println(
String.format("%s يتلقى تغيير %s، حالة المراقبة الحالية هي %s"،
الاسم، الحدث، events.getState()));
}
}
}
يبدو الأمر مثاليًا، لكنه لا يزال غير مثالي. لأن الأحداث يتم ترميزها بشكل ثابت كخصائص لفئة المراقب. بهذه الطريقة، يتم تحديد نوع الحدث في وقت الترجمة. إذا كنت تريد إضافة نوع حدث جديد، فيجب عليك تعديل واجهة IObservable وفئة Observable، مما يقلل المرونة بشكل كبير.
إنه يعادل اقتران الراصد بهذه الأحداث المحددة، فكيف نكسر هذا القيد؟
الجواب هو تقديم مكون جديد والسماح لهذا المكون بإدارة العلاقة بين الأحداث والمراقبين والكائنات المرصودة. عند وقوع حدث ما، سيستدعي هذا المكون أيضًا وظيفة رد الاتصال الخاصة بالمراقب. يعد هذا أيضًا نوعًا من الفصل، وهو مشابه إلى حد ما لحاوية Spring's IOC.
أما بالنسبة للتنفيذ المحدد، أعتقد أن Guava EventBus قام بعمل جيد جدًا، يمكنك الرجوع إلى الرابط الذي ذكرته سابقًا.
ملاحظة: هذا المنشور ليس إعلانًا عن Guava EventBus، لكن أفكاري الخاصة يتم تطويرها خطوة بخطوة، وتتوافق تدريجيًا مع أفكار تصميم Guava EventBus.
دعنا نستمر في إلقاء نظرة على مثال فئة JDK القياسية التي تنفذ نمط المراقب، ثم نحلل تنفيذ التعليمات البرمجية المصدر الخاصة بها. كل ما نحتاج إلى النظر إليه هو فئة Observable وواجهة Observer.
تطبق فئات JDK القياسية نمط المراقب
انسخ رمز الكود كما يلي:
import java.util.Observable;
import java.util.Observer;
/**
* تنفيذ نمط المراقب باستخدام الفئات القياسية في الحزمة java.util
* @المؤلف سوف
*
*/
الطبقة العامة JDKObserverDemo {
public static void main(String[] args) {
JDKObserverDemo jod = new JDKObserverDemo();
// المراقب
MyObservable myObservable = jod.new MyObservable("hello");
// المراقب
Observer myObserver = jod.new MyObserver();
// يسجل
myObservable.addObserver(myObserver);
// قم بتغيير الحالة المرصودة وتشغيل وظيفة رد اتصال المراقب
myObservable.setValue("will");
}
فئة MyObservable تمتد إلى Observable {
سلسلة خاصة تمت مراقبتها // القيمة المرصودة
MyObservable العامة (سلسلة شاهدت القيمة) {
this.watchedValue = watchedValue;
}
مجموعة الفراغ العام (سلسلة newValue) {
إذا (!watchedValue.equals(newValue)) {
WatchedValue = newValue;
setChanged();
notifyObservers(newValue);
}
}
@تجاوز
سلسلة عامة إلى سلسلة () {
إرجاع "MyObservable";
}
}
فئة MyObserver تنفذ المراقب {
@تجاوز
تحديث باطل عام (Observable o، Object arg) {
System.out.println(o + "تم تغيير الحالة، الوسيطة هي:" + arg);
}
}
}
بعد النظر في تنفيذ Observer وObservable في مكتبة JDK القياسية، أصبح الأمر بسيطًا جدًا، لذلك لا أريد أن أقول المزيد.
فيما يلي تنفيذ المستمع في الكوارتز.
الكوارتز جدولة المستمع
انسخ رمز الكود كما يلي:
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* فئة الكوارتز الأساسية، أي ما يعادل الملاحظة (الملاحظة)
* @المؤلف سوف
*
*/
فئة عامة كوارتز جدولة {
Private ArrayList<SchedulerListener> InternalSchedulerListeners = new ArrayList<SchedulerListener>(10);
//private ArrayList<JobListener> interanlJobListeners = new ArrayList<JobListener>(); // يمكن أن يحتوي Observable على مجموعات متعددة من المستمعين
تاريخ الجدول الزمني العام (مشغل الزناد) {
إذا (الزناد == فارغة) {
عودة فارغة؛
}
System.out.println("جدولة المهمة، المشغل: " + Trigger);
notifySchedulerListenersScheduled(trigger);
إرجاع تاريخ جديد ()؛
}
الفراغ العام unScheduleJob (مشغل الزناد) {
إذا (الزناد == فارغة) {
يعود؛
}
System.out.println("إلغاء جدولة المهمة، المشغل: " + Trigger);
notifyShedulerListenerUnScheduled(trigger);
}
// قم بالتسجيل في برنامج ScholarListener
public void addInternalSchedulerListener(SchedulerListener جدولةListener) {
متزامن (internalSchedulerListeners) {
InternalSchedulerListeners.add(schedulerListener);
}
}
// إزالة جدولةListener
إزالة منطقية عامة InternalSchedulerListener(SchedulerListener جدولةListener) {
متزامن (internalSchedulerListeners) {
إرجاع InternalSchedulerListeners.remove(schedulerListener);
}
}
القائمة العامة <SchedulerListener> getInternalSchedulerListeners() {
متزامن (internalSchedulerListeners) {
return java.util.Collections.unmodifiableList(new ArrayList<SchedulerListener>(internalSchedulerListeners));
}
}
public void notifySchedulerListenersScheduled(Trigger Trigger) {
for(مستمع SchedulerListener: getInternalSchedulerListeners()) {
listener.jobScheduled(trigger);
}
}
public void notifyShedulerListenerUnScheduled(Trigger Trigger) {
for(مستمع SchedulerListener: getInternalSchedulerListeners()) {
listener.jobUnScheduled(trigger);
}
}
}
SchollerListener
انسخ رمز الكود كما يلي:
// واجهة الاستماع، وظيفة رد الاتصال، يحتاج العميل إلى توفير تنفيذ وظيفة رد الاتصال عند التسجيل للمراقبة
واجهة عامة جدولةListener {
مهمة باطلة (مشغل الزناد) ؛
وظيفة باطلةUnScheduled(مشغل الزناد);
}
مشغل
انسخ رمز الكود كما يلي:
//مشغل
مشغل الطبقة العامة {
سلسلة خاصة بمفتاح التشغيل؛
اسم مشغل السلسلة الخاصة؛
المشغل العام (سلسلة المشغل، سلسلة المشغل اسم) {
this.triggerKey = TriggerKey;
this.triggerName = TriggerName;
}
سلسلة عامة getTriggerKey() {
إرجاع مفتاح التشغيل؛
}
مجموعة الفراغ العام TriggerKey (سلسلة TriggerKey) {
this.triggerKey = TriggerKey;
}
سلسلة عامة getTriggerName() {
إرجاع اسم المشغل؛
}
مجموعة الفراغ العام (اسم المشغل سلسلة) {
this.triggerName = TriggerName;
}
سلسلة عامة إلى سلسلة () {
return String.format("{triggerKey: %s, TriggerName: %s}", TriggerKey, TriggerName);
}
}
امتحان
انسخ رمز الكود كما يلي:
اختبار الطبقة العامة {
public static void main(String[] args) {
QuartzScheduler qs = new QuartzScheduler();
جدولة ليستنر المستمعA = جديد جدولةListener () {
@تجاوز
وظيفة باطلة عامة UnScheduled (مشغل الزناد) {
System.out.println("مهمة الاستماع غير مجدولة: " + Trigger.getTriggerName());
}
@تجاوز
وظيفة فارغة عامة (مشغل الزناد) {
System.out.println("مهمة المستمع مجدولة: " + Trigger.getTriggerName());
}
};
جدولة ليستنر المستمعB = جديد جدولةListener () {
@تجاوز
وظيفة باطلة عامة UnScheduled (مشغل الزناد) {
System.out.println("مهمة listenerB غير مجدولة: " + Trigger.getTriggerName());
}
@تجاوز
وظيفة فارغة عامة (مشغل الزناد) {
System.out.println("مهمة المستمع المجدولة: " + Trigger.getTriggerName());
}
};
// تسجيل مستمع المجدول
qs.addInternalSchedulerListener(listenerA);
qs.addInternalSchedulerListener(listenerB);
Trigger TriggerA = new Trigger("Key1", "triggerA");
Trigger TriggerB = new Trigger("Key2", "triggerB");
qs.scheduleJob(triggerA);
qs.scheduleJob(triggerB);
qs.unScheduleJob(triggerA);
}
}