Implémentation simple d'un modèle d'observateur
Copiez le code comme suit :
importer java.util.ArrayList ;
importer java.util.Collections ;
importer java.util.Iterator ;
importer java.util.List ;
/**
* Les rappels sont utilisés en mode observateur :
* A. L'observateur s'enregistre sur la liste des auditeurs des observés, et la classe observateur elle-même fournit une fonction de rappel
* B. L'Observable (Observable ou Sujet) tient à jour une liste d'observateurs et peut enregistrer et désinscrire des observateurs
* C. Une fois le statut de l'observateur modifié, il peut appeler notifyObservers(). Cette méthode parcourra la liste des observateurs et l'appellera un par un.
fonction de rappel fournie par l'observateur
* @author le fera
*
*/
classe publique SimpleObserverPattern {
public static void main (String[] arguments) {
SimpleObserverPattern sop = new SimpleObserverPattern();
List<IObserver> observateurs = new ArrayList<IObserver> ();
IObserver observerA = sop.new Observer("ObserverA");
IObserver observerB = sop.new Observer("ObserverB");
observateurs.add(observateurA);
observateurs.add(observerB);
IObservable observable = sop.new Observable(observateurs);
observable.registerObserver(sop.new Observer("ObserverC"));
observable.changeState();
observable.close();
}
// La personne observée est appelée Sujet à certains endroits.
interface IObservable {
void registerObserver (observateur IObserver);
void unregisterObserver (observateur IObserver);
void notifyObservers();
Chaîne getState();
void changeState();
annuler fermer();
}
la classe Observable implémente IObservable {
chaîne finale statique privée NEW = "Nouveau" ;
chaîne finale statique privée CHANGED = "Modifié" ;
chaîne finale statique privée CLOSED = "Fermé" ;
État de chaîne privé ;
observateurs privés List<IObserver> ;
public Observable() {
ceci (nul);
}
public Observable (Liste <IObserver> observateurs) {
if(observateurs == null) {
observateurs = new ArrayList<IObserver> ();
}
this.observers = Collections.synchronizedList(observateurs);
this.state = NOUVEAU ;
}
@Outrepasser
public void registerObserver (observateur IObserver) {
observateurs.add(observateur);
}
@Outrepasser
public void unregisterObserver (observateur IObserver) {
observateurs.remove(observateur);
}
@Outrepasser
public void notifyObservers() {
Iterator<IObserver> iter = observers.iterator();
while(iter.hasNext()) {
iter.next().update(this);
}
}
@Outrepasser
chaîne publique getState() {
état de retour ;
}
@Outrepasser
public void changeState() {
this.state = CHANGÉ ;
notifyObservateurs();
}
@Outrepasser
public void close() {
this.state = FERMÉ ;
notifyObservateurs();
}
}
interface IObservateur {
void update (IObservable observalbe);
}
class Observer implémente IObserver {
nom de chaîne privé ;
public Observer (nom de la chaîne) {
this.name = nom ;
}
@Outrepasser
mise à jour publique void (IObservable observalbe) {
System.out.println(
String.format("%s reçoit la modification de l'observatoire, l'état actuel de l'observatoire est %s",
nom, observalbe.getState()));
}
}
}
L'implémentation ci-dessus utilise directement l'objet observé comme paramètre de fonction de rappel. Ceci est très inélégant et peut fonctionner dans des scénarios simples.
Mais en fait, dans la plupart des cas, une personne observée a de nombreux événements ou états, et chaque observateur peut être intéressé par différents événements ou états, ou dans le but de cacher des informations, nous ne voulons pas que chaque observateur puisse accéder à tous les états. à l’intérieur de l’Observable.
De cette façon, j'ai continué à faire évoluer le code vers la version suivante. Notez que je n'ai pas examiné les problèmes de concurrence en détail ici.
Copiez le code comme suit :
importer java.util.Collections ;
importer java.util.HashSet ;
importer java.util.Hashtable ;
importer java.util.Iterator ;
importer java.util.Set ;
classe publique MultiEventObserverPattern {
public static void main (String[] arguments) {
MultiEventObserverPattern meop = new MultiEventObserverPattern();
IObservable observable = meop.new Observable();
IObserver observerA = meop.new Observer("ObserverA");
IObserver observerB = meop.new Observer("ObserverB");
//Enregistrer les événements d'intérêt
observable.registerObserver(observable.getEventA(), observerA);
observable.registerObserver(observable.getEventB(), observerB);
//Changer l'état observé
observable.changeStateA();
observable.changeStateB();
}
interface IEvent {
void eventChange();
Chaîne getState();
}
la classe EventA implémente IEvent {
private static final String INITIALIZED = "Initialisé" ;
private static final String PENDING = "En attente";
État de chaîne privé ;
public ÉvénementA() {
this.state = INITIALISE;
}
@Outrepasser
public void eventChange() {
System.out.println("Changement d'événementA");
this.state = EN ATTENTE ;
}
@Outrepasser
chaîne publique toString() {
renvoyer "ÉvénementA" ;
}
@Outrepasser
chaîne publique getState() {
état de retour ;
}
}
la classe EventB implémente IEvent {
chaîne finale statique privée NEW = "Nouveau" ;
chaîne finale statique privée IDLE = "Idle" ;
État de chaîne privé ;
public ÉvénementB() {
this.state = NOUVEAU ;
}
@Outrepasser
public void eventChange() {
System.out.println("Changement d'événementB");
this.state = IDLE ;
}
@Outrepasser
chaîne publique toString() {
renvoyer "ÉvénementB" ;
}
@Outrepasser
chaîne publique getState() {
état de retour ;
}
}
// Observable, parfois appelé Sujet
interface IObservable {
void registerObserver (événement IEvent, observateur IObserver);
void unregisterObserver (événement IEvent, observateur IObserver);
// Notifier les observateurs qu'un événement s'est produit
void notifyObservers (événement IEvent);
void changeStateA();
void changeStateB();
IEventgetEventA();
IEventgetEventB();
}
la classe Observable implémente IObservable {
événement IEvent privéA ;
événement IEvent privéB ;
private Hashtable<IEvent, Set<IObserver>> eventObserverMapping ;
public Observable() {
ceci (nul);
}
// Si certains des Set<IObserver> transmis par evenObserverMapping n'ont pas été modifiés de manière synchrone, vous ne pouvez rien y faire.
public Observable(Hashtable<IEvent, Set<IObserver>> eventObserverMapping) {
if(eventObserverMapping == null) {
eventObserverMapping = new Hashtable<IEvent, Set<IObserver>> ();
}
this.eventObserverMapping = new Hashtable<IEvent, Set<IObserver>> ();
this.eventA = new EventA();
this.eventB = new EventB();
}
@Outrepasser
public void registerObserver (événement IEvent, observateur IObserver) {
Set<IObserver> observers = eventObserverMapping.get(event);
if(observateurs == null) {
observateurs = Collections.synchronizedSet(new HashSet<IObserver> ());
observateurs.add(observateur);
eventObserverMapping.put(événement, observateurs);
}
autre {
observateurs.add(observateur);
}
}
@Outrepasser
public void unregisterObserver (événement IEvent, observateur IObserver) {
Set<IObserver> observers = eventObserverMapping.get(event);
if(observateurs != null) {
observateurs.remove(observateur);
}
}
@Outrepasser
public void notifyObservers (événement IEvent) {
Set<IObserver> observers = eventObserverMapping.get(event);
if(observateurs != null && observateurs.size() > 0) {
Iterator<IObserver> iter = observers.iterator();
while(iter.hasNext()) {
iter.next().update(event);
}
}
}
@Outrepasser
public void changeStateA() {
// Changer l'état A déclenchera l'événement A
eventA.eventChange();
notifyObservers(eventA);
}
@Outrepasser
public void changeStateB() {
// Changer l'état B déclenchera l'événement B
eventB.eventChange();
notifyObservers(eventB);
}
@Outrepasser
public IEvent getEventA() {
retourner l'événementA ;
}
@Outrepasser
public IEvent getEventB() {
retourner l'événementB ;
}
}
interface IObservateur {
void update (événement IEvent);
}
class Observer implémente IObserver {
nom de chaîne privé ;
public Observer (Nom de la chaîne) {
this.name = nom ;
}
@Outrepasser
mise à jour publique void (événement IEvent) {
System.out.println(
String.format("%s reçoit la modification de %s, l'état actuel de l'observatoire est %s",
nom, événement, event.getState()));
}
}
}
Cela semble parfait, mais ce n'est toujours pas parfait. Parce que les événements sont codés en dur en tant que propriétés de la classe Observer. De cette façon, le type d'événement est déterminé au moment de la compilation. Si vous souhaitez ajouter un nouveau type d'événement, vous devez modifier l'interface IObservable et la classe Observable, ce qui réduit considérablement la flexibilité.
Cela équivaut à ce que l’observateur soit couplé à ces événements spécifiques, alors comment briser cette limitation ?
La réponse est d'introduire un nouveau composant et de laisser ce composant gérer la relation entre les événements, les observateurs et les objets observés. Lorsqu'un événement se produit, ce composant appellera également la fonction de rappel de l'observateur. C'est aussi une sorte de découplage, quelque peu similaire au conteneur IOC de Spring.
En ce qui concerne l'implémentation spécifique, je pense que Guava EventBus a fait du très bon travail. Vous pouvez vous référer au lien que j'ai mentionné plus tôt.
PS : Cet article n'est pas une publicité pour Guava EventBus, mais mes propres idées avancent étape par étape et, progressivement, elles correspondent aux idées de conception de Guava EventBus.
Continuons à regarder l'exemple de la classe standard JDK implémentant le modèle Observer, puis analysons son implémentation du code source. Tout ce que nous devons examiner est une classe Observable et une interface Observer.
Les classes standard du JDK implémentent le modèle d'observateur
Copiez le code comme suit :
importer java.util.Observable ;
importer java.util.Observer ;
/**
* Implémentez le modèle d'observateur à l'aide des classes standard du package java.util
* @author le fera
*
*/
classe publique JDKObserverDemo {
public static void main (String[] arguments) {
JDKObserverDemo jod = new JDKObserverDemo();
// Observateur
MonObservable monObservable = jod.new MonObservable("bonjour");
// Observateur
Observateur monObservateur = jod.new MonObservateur();
// registre
monObservable.addObserver(monObservateur);
//Modifier l'état observé et déclencher la fonction de rappel de l'observateur
monObservable.setValue("will");
}
la classe MyObservable étend Observable {
private String watchValue ; // la valeur observée
public MyObservable (String watchValue) {
this.watchedValue = watchValue ;
}
public void setValue (String newValue) {
if(!watchedValue.equals(newValue)) {
watchValue = newValue ;
setChanged();
notifyObservers (nouvelle valeur);
}
}
@Outrepasser
chaîne publique versChaîne() {
renvoyer "MonObservable" ;
}
}
la classe MyObserver implémente Observer {
@Outrepasser
public void update (Observable o, Object arg) {
System.out.println(l'état de o + " a changé, l'argument est : " + arg);
}
}
}
Après avoir examiné l'implémentation d'Observer et Observable dans la bibliothèque standard du JDK, c'est très simple, donc je ne veux pas en dire plus.
Vous trouverez ci-dessous l'implémentation de l'écouteur dans Quartz.
Écouteur QuartzScheduler
Copiez le code comme suit :
importer java.util.ArrayList ;
importer java.util.Date ;
importer java.util.List ;
/**
* Classe de noyau de quartz, équivalente à Observable (observable)
* @author le fera
*
*/
classe publique QuartzScheduler {
private ArrayList<SchedulerListener> internalSchedulerListeners = new ArrayList<SchedulerListener>(10);
//private ArrayList<JobListener> interanlJobListeners = new ArrayList<JobListener>(); // Un observable peut contenir plusieurs ensembles d'écouteurs
public Date planningJob (Déclencheur) {
si (déclencheur == nul) {
renvoie null ;
}
System.out.println("Planifier le travail, déclencheur : " + déclencheur);
notifySchedulerListenersScheduled(trigger);
renvoie une nouvelle date ();
}
public void unScheduleJob (déclencheur) {
si (déclencheur == nul) {
retour;
}
System.out.println("Déplanifier le travail, déclencheur : " + déclencheur);
notifyShedulerListenerUnScheduled(trigger);
}
//Enregistrer SchedulerListener
public void addInternalSchedulerListener (SchedulerListener planificateurListener) {
synchronisé (internalSchedulerListeners) {
internalSchedulerListeners.add(schedulerListener);
}
}
// Supprimer SchedulerListener
public boolean removeInternalSchedulerListener (SchedulerListener planificateurListener) {
synchronisé (internalSchedulerListeners) {
return internalSchedulerListeners.remove(schedulerListener);
}
}
public List<SchedulerListener> getInternalSchedulerListeners() {
synchronisé (internalSchedulerListeners) {
return java.util.Collections.unmodifiableList(new ArrayList<SchedulerListener>(internalSchedulerListeners));
}
}
public void notifySchedulerListenersScheduled (Déclencheur) {
for(écouteur SchedulerListener : getInternalSchedulerListeners()) {
écouteur.jobScheduled(trigger);
}
}
public void notifyShedulerListenerUnScheduled (déclencheur) {
for(écouteur SchedulerListener : getInternalSchedulerListeners()) {
écouteur.jobUnScheduled(trigger);
}
}
}
PlanificateurListener
Copiez le code comme suit :
// Interface d'écoute, fonction de rappel, le client doit fournir l'implémentation de la fonction de rappel lors de son inscription à la surveillance
interface publique SchedulerListener {
void jobScheduled (déclencheur);
void jobUnScheduled (déclencheur);
}
Déclenchement
Copiez le code comme suit :
//Déclenchement
Déclencheur de classe publique {
clé de déclenchement de chaîne privée ;
chaîne privée nom du déclencheur ;
déclencheur public (String triggerKey, String triggerName) {
this.triggerKey = triggerKey;
this.triggerName = triggerName ;
}
chaîne publique getTriggerKey() {
retourner la clé de déclenchement ;
}
public void setTriggerKey(String triggerKey) {
this.triggerKey = triggerKey;
}
chaîne publique getTriggerName() {
retourner le nom du déclencheur ;
}
public void setTriggerName (String triggerName) {
this.triggerName = triggerName ;
}
chaîne publique toString() {
return String.format("{triggerKey : %s, triggerName : %s}", triggerKey, triggerName);
}
}
Test
Copiez le code comme suit :
Test de classe publique {
public static void main (String[] arguments) {
QuartzScheduler qs = new QuartzScheduler();
SchedulerListener écouteurA = new SchedulerListener() {
@Outrepasser
public void jobUnScheduled (Déclencheur) {
System.out.println("listenerA job non planifié : " + trigger.getTriggerName());
}
@Outrepasser
public void jobScheduled (déclencheur) {
System.out.println("listenerA job planifié : " + trigger.getTriggerName());
}
} ;
SchedulerListener écouteurB = new SchedulerListener() {
@Outrepasser
public void jobUnScheduled (Déclencheur) {
System.out.println("tâche d'auditeurB non planifiée : " + trigger.getTriggerName());
}
@Outrepasser
public void jobScheduled (déclencheur) {
System.out.println("tâche d'auditeurB planifiée : " + trigger.getTriggerName());
}
} ;
//Enregistrer l'écouteur du planificateur
qs.addInternalSchedulerListener(écouteurA);
qs.addInternalSchedulerListener(écouteurB);
Déclencheur triggerA = new Trigger("Key1", "triggerA");
Déclencheur triggerB = new Trigger("Key2", "triggerB");
qs.scheduleJob(triggerA);
qs.scheduleJob(triggerB);
qs.unScheduleJob(triggerA);
}
}