Pratiquer le design-pattern Observer en 15 min

Les design-pattern (ou motifs de conception) réalisent le principe de ne pas réinventer la roue en capitalisant les expériences.

En effet, un design-pattern décrit à la fois :

  • Les situations de conception que l'on rencontre trop souvent dans le développement de logiciels
  • Les solutions types (abstraites ou de haut niveau) identifiées pour ces problèmes (formelles ou non, indépendamment des langages objets)
  • Les bénéfices d'utiliser ces solutions dans le développement de logiciels.

Les design-pattern offrent donc une description complète des problèmes répétitifs et les solutions abstraites retenues en expliquant les conséquences à l'usage.
Cette description reste indépendante du langage objet et voire du métier développement de logiciels.
Néanmoins, notons que les design ne sont pas des briques logicielles.
Les design-pattern se divisent en trois groupes: les design de création, de structure et de comportement.
Dans ce billet, nous mettons en pratique le design-pattern "observer" faisant partie du dernier groupe.
L'exemple choisi est volontairement simpliste néanmoins il illustre bien une problématique réelle. L'exemple pratique s'appuie sur les classes/interfaces du package java.util
Le seul pré-requis est une petite dose d'abstraction.

Le design-pattern observer

Situation:

Le besoin consiste à notifier tout changement de l'état d'un objet aux objets dépendants (pour rester simple, la notification est unidirectionnelle).
Le premier objet est appelé "observable" ou observé et les objets qui dépendent se nomment "observer" ou observateurs.
Les observateurs peuvent répercuter le changement notifié de façon dynamique.
La dépendance entre objet observable et objets observateurs est également gérée dynamiquement : On peut ajouter/retirer un observateur à tout moment sans bien sûr affecter le code des classes existantes.

Solution(avec API JAVA):

La classe de l'objet "observé" doit étendre la classe "Observable" et ainsi l'objet ajoute un observateur via la méthode addObserver,
Les objets observateurs doivent implémenter l'interface Observer et par conséquent implémenter la méthode "void update(Observable obs, Object obj)",
Au moment du changement dans l'état de l'objet observé, notifier les observateurs (par exemple, sur le setter de cet attribut).
NB. Tous les détails sont données dans la partie mise en pratique ci-après.

Avantages:

  • Ne pas réinventer la roue,
  • La solution offerte par les API Java répond à la situation posée et est robuste,
  • Facile à mettre en place.

Mise en pratique:

Une entreprise (siège) peut avoir ou être liée à plusieurs établissements. On souhaite notifier tout changement survenu dans "Entreprise" aux objets liés, de type "Etablissement" et ajoutés comme observateurs.

Le code des classes java ci-après montre comment notifier dynamiquement tout changement dans l'objet observé aux observateurs inscrits.
On commence par écrire une classe "Entreprise" de l'objet observé. Celle-ci étend la classe Observable et doit notifier les observateurs via les deux méthodes "setChanged" et "notifyObservers" de la classe mère Observable comme le montre le code java qui suit.
Ensuite, on donne le code la classe "Etablissement" qui implémente l'interface "Observer".

//la classe Entreprise 
public class Entreprise extends Observable {
	 private int id;
	 private boolean etat;
	 public void setEtat(boolean etat){
		this.etat=etat;		
		setChanged();
		notifyObservers(this.etat);		
	 } 
	 public boolean isEtat(){ return etat;}
	//autre setters/getters ...
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String toString(){
		return "Entreprise (id="+id+" etat="+etat+")";
	}
}

De l'autre côté, la classe "observateur" doit implémenter l'interface "Observer" ce qui exige de définir la méthode "void update(Observable obs, Object obj)".
C'est dans cette méthode qu'il faut indiquer le code métier qui sera exécuté à chaque modification de l'attribut surveillé de l'objet "Entreprise".
Bien évidemment, l'objet observé doit ajouter/déclarer un ou plusieurs observateur(s) pour que le processus de notification soit activé.
Dans l'exemple, à chaque fois que l'attribut "etat" change, une notification est envoyée aux observateurs "Etablissement".
A la réception de la notification, chaque observateur "Etablissemet" actualise l'attribut "infos" avec la chaîne "Notification reçu le...".
Notez que la réécriture de la méthode toString() de la classe Etablissement permet d'afficher sur la console cet objet avant et après toute modification de l'état de l'objet "Entreprise".

public class Etablissement implements Observer{
 
	private int id;
	private String infos;
	public void update(Observable obs, Object obj) {
		if(obs instanceof Entreprise){
			Calendar calendar=new GregorianCalendar(Locale.FRANCE);
			Date date=calendar.getTime();
			setInfos("Notification recu le "+new SimpleDateFormat().format(date));
		}
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getInfos() {
		return infos;
	}
	public void setInfos(String infos) {
		this.infos = infos;
	}
	public String toString(){
		return "Etablissement ( id="+id+" infos :"+infos+")";
	}
}

Et on termine avec le client (Main.java) qui illustre les différentes notions expliquées:

public class Main{
 
		//définir un etablissement
		Etablissement etab=new Etablissement();
		etab.setId(1);
		etab.setInfos("RAS");
 
		//definir une entreprise
		Entreprise ent=new Entreprise();
		//Ajouter un observer	
		ent.addObserver(etab);
		System.out.print("Avant mise à jour de l'etat de l'entreprise:
\tEtab: "+ etab);
		//Effectuer un changement sur l'attribut etat de l'entreprise
		ent.setEtat(true);	
		System.out.print("Après mise à jour de l'etat de l'entreprise:
\tEtab: "+ etab);
}

L'exécution de ce code affiche sur la console:

Avant mise à jour de l'etat de l'entreprise:
	Etab: Etablissement ( id=1 infos :RAS)
Après mise à jour de l'etat de l'entreprise:
	Etab: Etablissement ( id=1 infos :Notification recu le 18/08/09 19:51)

Résumons les étapes nécessaires:

  1. - La classe de l'objet observé doit étendre la classe "Observable". Elle doit également appeler les méthodes "setChanged" et "notifyObservers" sur l'attribut à surveiller.
  2. - La classe de l'observateur doit implémenter l'interface "Observer" et par conséquent définir la méthode "void update(Observable obs, Object obj)" avec le code métier approprié.
  3. - Enfin, dans le client ajouter à l'observable son/ses observateurs via la méthode "addObserver".

Conclusion

Vous avez entre les mains un design qui va vous faciliter certaines tâches de développement.
L'usage de l'API robuste de Java vous épargne les difficultés de coder par vous-même les notifications entre objets et la gestion de multithread liée.
Vous pourriez dire qu'avec ce design les changements entre entités liées sont maitrisées avec un coût réduit.
Dans un prochain billet, je vous présente un nouveau design qui nous (vous) permettra de supporter au mieux (ne le dites pas fort!) les demandes incessantes du chef de projet (et du client) de faire changer et rechanger le code des règles métier durant le développement du projet.

Donc à la prochaine fois... pour parler de stratégie!

7 commentaires

  1. Merci de cette explication concise et précise. J’avais difficile à comprendre le comportement réel entre Observable et Observer (peut être qu’il aurait pu choisir d’autres mots que des mots si proches).

  2. Merci pour exemple de design pattern observer;Il m’a permis de comprendre le modèle et son utilisation

  3. Please, everyone using this snippet and not getting a compile time error: say Hooo!

    public class Main{

    //définir un etablissement
    Etablissement etab=new Etablissement();
    etab.setId(1);
    etab.setInfos(« RAS »);

    //definir une entreprise
    Entreprise ent=new Entreprise();
    //Ajouter un observer
    ent.addObserver(etab);
    System.out.print(« Avant mise à jour de l’etat de l’entreprise:
    \\tEtab: « + etab);
    //Effectuer un changement sur l’attribut etat de l’entreprise
    ent.setEtat(true);
    System.out.print(« Après mise à jour de l’etat de l’entreprise:
    \\tEtab: « + etab);
    }

  4. Salut 🙂
    je vous remercie beaucoup de nous avoir expliquer clairement comment utiliser ce Design Pattern « Observer » …
    Votre Blog est classé parmi mes favoris 😉

  5. Bonjour,
    Comment intégrer votre programme dans l’environnement eclipse? J’ai essayé, j’ai eu beaucoup d’erreurs. Merci de répondre.

  6. Bonjour @Saliha
    Je ne comprends pas très bien votre attente mais vous pouvez partir d’un projet standard dans Eclipse (Menu File->create java project) puis créer les 2 classes POJO Entreprise et Etablissement ensuite compléter la méthode main avec le code fourni. Analyser l’exécution de ce la classe main.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *