Observer pattern : que recouvre l’Observer design pattern ?
Divers aspects doivent être observés dans la programmation de logiciels : le produit final doit non seulement disposer des fonctionnalités souhaitées mais également d’un code source aussi lisible et intelligible que possible. Les efforts engagés pour y parvenir doivent être aussi faibles que possible, en particulier lorsque la conception des programmes ou des parties de programmes inclut des fonctionnalités ou des éléments récurrents. Grâce aux patrons ou modèles GoF (« Gang of Four »), les développeurs disposent d’une palette de solutions prédéfinies visant à répondre à différentes problématiques de la conception logicielle.
En dehors des autres patrons connus tels que le patron visiteur ou le patron singleton, cette palette de patrons de conception pratiques inclut également le patron Observateur qui permet de simplifier considérablement le quotidien en programmation. Nous vous expliquons ce que recouvre l’Observer design pattern (avec une représentation graphique en UML) et vous présentons les atouts et les failles de ce modèle.
Qu’est-ce que l’Observer pattern (patron de conception Observateur) ?
L’Observer design pattern, abrégé en Observer pattern et traduit par « patron de conception Observateur », est l’un des modèles les plus appréciés dans la conception de logiciels. Il fournit en effet une méthode uniforme permettant de définir un rapport de dépendance entre deux objets ou plus afin de communiquer l’ensemble des modifications à un autre objet donné de façon aussi simple et rapide que possible. À cette fin, il est possible d’enregistrer n’importe quel objet comme « observer » ou observateur d’un autre objet. Ce dernier objet, que l’on appelle « sujet », informe l’observateur enregistré dès qu’il a été modifié ou adapté.
Comme évoqué auparavant, l’Observer pattern fait partie des « modèles GoF » publiés en 1994 dans « Design Patterns: Elements of Reusable Object-Oriented Software ». La vingtaine de solutions pour la conception logicielle décrite dans cet ouvrage joue aujourd’hui un rôle essentiel dans la conception et l’élaboration des applications informatiques.
But et fonctionnement de l’Observer pattern
Le patron de conception Observateur utilise deux types d’acteurs : d’une part le sujet, c’est-à-dire l’objet dont le statut doit être observé sur la durée ; d’autre part, les objets observant (observers ou observateurs) qui souhaitent être informés de toutes les modifications apportées au sujet.
Habituellement, on attribue plusieurs observateurs à un sujet. Toutefois, l’Observer pattern peut en principe être utilisé pour un seul objet observateur.
En l’absence du patron de conception Observateur, les observateurs devraient demander régulièrement au sujet des mises à jour sur son statut. Chaque requête individuelle impliquerait un temps de calcul correspondant ainsi que les ressources matérielles nécessaires à cet effet. L’idée de base de l’Observer pattern est de centraliser la tâche d’information sur le sujet. À cette fin, il crée une liste dans laquelle les observateurs peuvent s’inscrire. En cas de modification, le sujet informe les observateurs dans l’ordre de leur inscription, sans que ces derniers aient besoin d’être actifs. Si on ne souhaite plus qu’un observateur soit automatiquement informé des mises à jour du statut, il suffit de le retirer de la liste.
Il existe deux méthodes pour informer les différents observateurs : dans la méthode « push », le sujet transmet le statut modifié dès la notification. Ceci peut toutefois entraîner des problèmes si des informations ne pouvant être exploitées par l’observateur sont transmises. Ce problème ne survient pas dans la méthode alternative que l’on appelle méthode « pull » : dans cette approche, le sujet indique uniquement que des modifications ont été apportées. Les observateurs doivent ensuite demander le statut modifié via un appel séparé.
Représentation graphique de l’Observer pattern (diagramme UML)
Le fonctionnement et l’utilisation des patrons de conception tels que l’Observer pattern sont souvent difficiles à comprendre pour des personnes extérieures. Une représentation graphique de ce modèle de conception permettra de faciliter la compréhension. Très répandu, le langage de modélisation UML (Unified Modeling Language) est idéal pour cette représentation puisqu’il permet de présenter les liens de façon claire et compréhensible pour les utilisateurs et les experts en applications. C’est la raison pour laquelle nous avons utilisé l’UML pour représenter l’Observer pattern de façon abstraite.
Quels sont les avantages et les inconvénients de l’Observer design pattern ?
L’utilisation de l’Observer pattern dans le développement logiciel peut s’avérer avantageuse dans de nombreuses situations. Le principal avantage de ce concept est le haut degré d’indépendance entre l’objet observé (sujet) et les observateurs qui s’appuient sur le statut actuel de cet objet. Par exemple, l’objet observé n’a nullement besoin de disposer d’informations sur ses observateurs puisque l’interaction peut être réalisée indépendamment via l’interface de l’observateur. Les observateurs reçoivent les mises à jour automatiquement ce qui supprime totalement les requêtes sans résultat puisque le sujet reste le même.
Cependant, le fait que le sujet informe automatiquement tous les observateurs enregistrés des modifications ne présente pas que des avantages : en effet, les informations sur la modification sont transmises même si elles ne sont pas pertinentes pour l’un des observateurs. Cela peut se révéler problématique si le nombre d’observateurs enregistrés est très important puisque le modèle Observateur pourra gaspiller un temps de calcul considérable dans la transmission. Un autre problème du patron de conception Observateur est que, bien souvent, le code source du sujet ne permet pas de déterminer quel observateur doit recevoir les informations.
Observer pattern : dans quels cas est-il utilisé ?
L’Observer design pattern est tout particulièrement utile dans les applications basées sur des composants dont le statut est
- d’une part, fortement observé par les autres composants et
- d’autre part, soumis à des modifications régulières.
Parmi les cas d’utilisation typiques, on trouve les IGU (Graphical User Interfaces), des interfaces servant à la communication avec un logiciel et permettant une utilisation facile par les utilisateurs. Dès que des données sont modifiées, elles doivent être actualisées dans tous les composants de l’IGU, une tâche qui peut être assurée de façon optimale grâce à la structure sujet/observateur de l’Observer pattern. Les programmes utilisant des ensembles de données à visualiser (tableaux classiques ou diagrammes graphiques) profitent également de l’organisation effectuée par ce modèle de conception.
En principe, l’Observer design pattern ne comporte aucune restriction en ce qui concerne le langage de programmation utilisé. Pour que l’implémentation de ce modèle soit pertinente, il suffit que le paradigme orienté objet soit supporté. Parmi les langages dans lesquels l’utilisation de ce patron est très appréciée, on trouve notamment C#, C++, Java, JavaScript, Python et PHP.
Observer pattern : exemple d’utilisation du patron de conception Observateur
L’implémentation exacte de l’Observer design pattern dans les différents langages de programmation peut fortement varier. L’idée de base reste toutefois la même : rendre plus accessible un objet donné ou son statut à une multitude d’autres objets.
Dans notre exemple, un texte publié par le « narrateur » doit être affiché dans les champs de texte de plusieurs « auditeurs ». À cet effet, la classe narrateur (le sujet) étend la classe Observable avec la méthode addObserver(). Ceci permet d’ajouter des auditeurs (les observateurs). Par ailleurs, la méthode setChanged() est introduite. Cette méthode enregistre les modifications apportées au sujet et appelle notifyObservers() en cas de modifications afin d’en informer tous les observateurs.
class narrateur extends Observable {
public narrateur(){
this.addObserver(new auditeur_1());
this.addObserver(new auditeur_2());
tell("texte");
}
public void tell(String info){
if(countObservers()>0){
setChanged();
notifyObservers(info);
}
}
}
Par ailleurs, les observateurs ont besoin d’une implémentation de l’interface observateur incluant la méthode udpate() et deux arguments : l’objet observé et la modification sous la forme d’une instance d’objet (ConcreteSubject).
class auditeur extends JFrame implements Observer{
private JTextField field;
public auditeur(){
field1 = new JTextField("a");
add(field);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 50);
setVisible(true);
}
public void update(Observable o, Object arg) {
field.setText((String) arg);
}
}