Le mode observateur, également connu sous le nom de mode publication / sous-être, a été proposé par le groupe Four-Person (GoF, à savoir Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides) en 1994 "Design Pattern: The Basics of Reusable Object-Oriented Software" (voir pages 293-313 dans le livre pour plus de détails). Bien que ce modèle ait une histoire considérable, il est toujours largement applicable à une variété de scénarios et est même devenu une partie intégrante de la bibliothèque Java standard. Bien qu'il existe déjà de nombreux articles sur les modèles d'observateurs, ils se concentrent tous sur la mise en œuvre en Java, mais ignorent les divers problèmes rencontrés par les développeurs lors de l'utilisation de modèles d'observateurs en Java.
L'intention originale de rédiger cet article est de combler cette lacune: cet article présente principalement la mise en œuvre du modèle d'observateur en utilisant l'architecture Java8, et explore plus en détail les problèmes complexes sur les modèles classiques sur cette base, y compris les classes internes anonymes, les expressions lambda, la sécurité des fils et la mise en œuvre de l'observateur non trivial. Bien que le contenu de cet article ne soit pas complet, de nombreux problèmes complexes impliqués dans ce modèle ne peuvent pas être expliqués dans un seul article. Mais après avoir lu cet article, les lecteurs peuvent comprendre ce qu'est le modèle d'observateur, son universalité en Java et comment faire face à des problèmes communs lors de la mise en œuvre du modèle d'observateur en Java.
Mode observateur
Selon la définition classique proposée par GOF, le thème du modèle d'observateur est:
Définit une dépendance un à plusieurs entre les objets. Lorsque l'état d'un objet change, tous les objets qui en dépendent sont notifiés et mis à jour automatiquement.
Qu'est-ce que ça veut dire? Dans de nombreuses applications logicielles, les états entre les objets sont interdépendants. Par exemple, si une application se concentre sur le traitement numérique des données, ces données peuvent être affichées via des tables ou des graphiques de l'interface utilisateur graphique (GUI) ou utilisé en même temps, c'est-à-dire lorsque les données sous-jacentes sont mises à jour, les composants de l'interface graphique correspondants doivent également être mis à jour. La clé du problème est de savoir comment mettre à jour les données sous-jacentes lorsque les composants de l'interface graphique sont mis à jour, et en même temps minimiser le couplage entre les composants de l'interface graphique et les données sous-jacentes.
Une solution simple et non évaluable consiste à se référer aux composants de la table et de l'interface graphique des objets qui gèrent ces données sous-jacentes, afin que les objets puissent informer les composants de l'interface graphique lorsque les données sous-jacentes changent. De toute évidence, cette solution simple a rapidement montré ses lacunes pour des applications complexes qui gèrent plus de composants de l'interface graphique. Par exemple, il existe 20 composants GUI qui s'appuient tous sur des données sous-jacentes, de sorte que les objets qui gèrent les données sous-jacentes doivent maintenir les références à ces 20 composants. À mesure que le nombre d'objets dépendant des données connexes augmente, le degré de couplage entre la gestion des données et les objets devient difficile à contrôler.
Une autre meilleure solution consiste à permettre aux objets de s'inscrire pour obtenir des autorisations pour mettre à jour les données d'intérêt, ce que le gestionnaire de données informera ces objets lorsque les données changent. En termes simples, laissez l'objet de données d'intérêt indique au gestionnaire: "Veuillez m'informer lorsque les données changent." De plus, ces objets peuvent non seulement s'inscrire pour obtenir des notifications de mise à jour, mais également annuler l'enregistrement pour s'assurer que le gestionnaire de données n'informe plus l'objet lorsque les données changent. Dans la définition d'origine de GOF, l'objet enregistré pour obtenir des mises à jour est appelé "observateur", le gestionnaire de données correspondant est appelé "sujet", les données qui l'intéressent est appelée "État cible", le processus d'enregistrement est appelé "ADD" et le processus d'observation de l'annulation est appelé "détachage". Comme mentionné ci-dessus, le mode observateur est également appelé le mode publication-subscription. On peut comprendre qu'un client s'abonne à l'observateur sur la cible. Lorsque le statut cible est mis à jour, la cible publie ces mises à jour de l'abonné (ce modèle de conception est étendu à une architecture générale, appelée l'architecture de publication-abonnement). Ces concepts peuvent être représentés par le diagramme de classe suivant:
ConcerteObserver l'utilise pour recevoir des modifications de l'état de mise à jour et transmettre une référence à la concereTesubject à son constructeur. Cela fournit une référence à un sujet spécifique pour un observateur spécifique, à partir de laquelle les mises à jour peuvent être obtenues lorsque l'état change. En termes simples, l'observateur spécifique sera informé de mettre à jour le sujet et d'utiliser en même temps les références de son constructeur pour obtenir l'état du sujet spécifique, et enfin stocker ces objets d'état de recherche sous la propriété ObserverState de l'observateur spécifique. Ce processus est illustré dans le diagramme de séquence suivant:
Professionnalisation des modèles classiques
Bien que le modèle d'observateur soit universel, il existe de nombreux modèles spécialisés, dont les plus courants sont les deux suivants:
Fournit un paramètre à l'objet d'état, transmis à la méthode de mise à jour appelée par l'observateur. En mode classique, lorsque l'observateur est informé que l'état du sujet a changé, son état mis à jour sera obtenu directement à partir du sujet. Cela nécessite que l'observateur enregistre une référence d'objet à l'état récupéré. Cela forme une référence circulaire, la référence de Concretsubject pointe vers sa liste d'observateurs, et la référence de ConcreteObserver pointe vers le concrètes qui peut obtenir l'état du sujet. En plus d'obtenir l'état mis à jour, il n'y a aucun lien entre l'observateur et le sujet qu'il enregistre à écouter. L'observateur se soucie de l'objet d'état, pas du sujet lui-même. C'est-à-dire que, dans de nombreux cas, ConcreteObserver et Concretsubject sont liés de force. Au contraire, lorsque Concretsubject appelle la fonction de mise à jour, l'objet d'état est transmis à ConcreteObserver et les deux n'ont pas besoin d'être associés. L'association entre ConcreteObserver et l'objet d'état réduit le degré de dépendance entre l'observateur et l'État (voir l'article de Martin Fowler pour plus de différences d'association et de dépendance).
Fusionnez la classe abstraite du sujet et le concrètes en une classe de singlesubject. Dans la plupart des cas, l'utilisation de classes abstraites dans le sujet n'améliore pas la flexibilité et l'évolutivité du programme, donc la combinaison de cette classe abstraite et de la classe de béton simplifie la conception.
Une fois ces deux modèles spécialisés combinés, le diagramme de classe simplifié est le suivant:
Dans ces modèles spécialisés, la structure de classe statique est grandement simplifiée et les interactions entre les classes sont également simplifiées. Le diagramme de séquence à ce moment est le suivant:
Une autre caractéristique du mode de spécialisation est l'élimination de l'observable de la variable membre de ConcreteObserver. Parfois, l'observateur spécifique n'a pas besoin de sauvegarder le dernier état du sujet, mais n'a besoin que de surveiller l'état du sujet lorsque l'état est mis à jour. Par exemple, si l'observateur met à jour la valeur de la variable membre à la sortie standard, il peut supprimer l'ObserverState, qui supprime l'association entre le CONCRETOBSERVER et la classe d'état.
Règles de dénomination plus courantes
Le modèle classique et même le modèle professionnel mentionné ci-dessus utilisent des termes tels que l'attachement, le détachement et l'observateur, tandis que de nombreuses implémentations Java utilisent différents dictionnaires, y compris le registre, le non-enregistrement, l'auditeur, etc. Il convient de mentionner que l'État est un terme général pour tous les objets dont l'auditeur doit surveiller les changements. Le nom spécifique de l'objet d'état dépend du scénario utilisé en mode observateur. Par exemple, dans le mode observateur de la scène où l'auditeur écoute l'occurrence de l'événement, l'auditeur enregistré recevra une notification lorsque l'événement se produira. L'objet de statut pour le moment est un événement, c'est-à-dire si l'événement s'est produit.
Dans les applications réelles, la dénomination des cibles comprend rarement un sujet. Par exemple, créez une application sur un zoo, enregistrez plusieurs auditeurs pour observer la classe de zoo et recevez des notifications lorsque de nouveaux animaux entrent dans le zoo. L'objectif dans ce cas est la classe de zoo. Afin de maintenir la terminologie cohérente avec le domaine de problème donné, le terme "sujet" ne sera pas utilisé, ce qui signifie que la classe de zoo ne sera pas nommée Zoosubject.
La dénomination de l'auditeur est généralement suivie par le suffixe de l'auditeur. Par exemple, l'auditeur mentionné ci-dessus pour surveiller les nouveaux animaux sera nommé AnimalAddedListener. De même, la dénomination de fonctions telles que le registre, le non-enregistrement et le notifie est souvent suffixée par leurs noms d'écoute correspondants. Par exemple, le registre, le non-inscription et les fonctions de notification de AnimalAddedListener seront nommés registeranimalAddedListener, UnregisteranImalAddedListener et notifieranimaladdedListeners. Il convient de noter que le nom de fonction notifier est utilisé, car la fonction notification gère plusieurs écouteurs plutôt qu'un seul auditeur.
Cette méthode de dénomination semblera longue et un sujet enregistrera généralement plusieurs types d'auditeurs. Par exemple, dans l'exemple du zoo mentionné ci-dessus, dans le zoo, en plus d'enregistrer de nouveaux auditeurs pour surveiller les animaux, il doit également enregistrer un auditeur aux animaux pour réduire les auditeurs. À l'heure actuelle, il y aura deux fonctions de registre: (registeranimalAddedListener et registeranimalremovedListener. De cette façon, le type d'écouteur est utilisé comme qualificatif pour indiquer le type d'observateur. Une autre solution consiste à créer une fonction RegisterListener, puis à la surcharger, mais la solution 1 peut savoir plus commodement quel écouteur est écouté. La surcharge est une approche de Niche relative.
Une autre syntaxe idiomatique consiste à utiliser sur le préfixe au lieu de la mise à jour, par exemple, la fonction de mise à jour est nommée onanimaladded au lieu de updateanimaladded. Cette situation est plus courante lorsque l'auditeur obtient des notifications pour une séquence, comme l'ajout d'un animal à la liste, mais il est rarement utilisé pour mettre à jour des données distinctes, comme le nom de l'animal.
Ensuite, cet article utilisera les règles symboliques de Java. Bien que les règles symboliques ne modifieront pas la conception et la mise en œuvre réelles du système, il est un principe de développement important d'utiliser des termes que les autres développeurs connaissent, vous devez donc être familier avec le modèle d'observateur des règles symboliques dans Java décrites ci-dessus. Le concept ci-dessus sera expliqué ci-dessous en utilisant un exemple simple dans l'environnement Java 8.
Un exemple simple
C'est également l'exemple du zoo mentionné ci-dessus. Utiliser l'interface API de Java8 pour implémenter un système simple, expliquant les principes de base du modèle d'observateur. Le problème est décrit comme:
Créez un zoo système, permettant aux utilisateurs d'écouter et d'annuler l'état d'ajouter un nouvel animal d'objet, et de créer un auditeur spécifique, responsable de la sortie du nom du nouvel animal.
Selon l'apprentissage précédent du modèle d'observateur, nous savons que pour implémenter une telle application, nous devons créer 4 classes, en particulier:
Classe de zoo: c'est-à-dire le thème du modèle, qui est responsable du stockage de tous les animaux dans le zoo et de la notification de tous les auditeurs enregistrés lorsque de nouveaux animaux se joignent.
Classe animale: représente un objet animal.
Classe AnimalAddedListener: c'est-à-dire l'interface d'observateur.
PrintNameanImalAddedListener: La classe d'observateurs spécifique est responsable de la sortie du nom de l'animal nouvellement ajouté.
Nous créons d'abord une classe animale, qui est un simple objet Java contenant des variables de membre, des constructeurs, des getters et des méthodes de setter. Le code est le suivant:
Classe publique Animal {Nom de chaîne privée; Animal public (nom de chaîne) {this.name = name;} public String getName () {return this.name;} public void setName (String name) {this.name = name;}}Utilisez cette classe pour représenter des objets animaux, puis vous pouvez créer l'interface AnimalAddedListener:
Interface publique AnimalAddededListener {public void onanimaladded (animal animal);}Les deux premières classes sont très simples, donc je ne les présenterai pas en détail. Ensuite, créez la classe de zoo:
classe publique Zoo {Liste privée <Animal> Animals = New ArrayList <> (); Liste privée <AnimalAddedListener> auditeurs = New ArrayList <> (); public void addanimal (Animal Animal) {// Ajouter la liste des animaux. RegisteranimalAddedListener (AnimalAddedListener Écouteur) {// Ajoutez l'auditeur à la liste des auditeurs enregistrésthis.Listeners.add (écouteur);} public void UnregisteranImalAddedListener (AnimalAddedListener Écouteur) {// Supprimez l'écouteur de la liste des auditeurs inscrits. notifyAnimalAddedListeners (animal animal) {// notify chacun des auditeurs de la liste des auditeurs enregistrés auditeurs.Cette analogie est complexe que les deux précédents. Il contient deux listes, l'une est utilisée pour stocker tous les animaux du zoo et l'autre est utilisé pour stocker tous les auditeurs. Étant donné que les objets stockés chez les animaux et les collections d'auditeur sont simples, cet article a choisi ArrayList pour le stockage. La structure de données spécifique de l'auditeur stocké dépend du problème. Par exemple, pour le problème du zoo ici, si l'auditeur a la priorité, vous devez choisir une autre structure de données ou réécrire l'algorithme de registre de l'auditeur.
La mise en œuvre de l'enregistrement et de la suppression est à la fois une méthode de délégué simple: chaque écouteur est ajouté ou supprimé de la liste d'écoute de l'écoute en tant que paramètre. La mise en œuvre de la fonction Notifie est légèrement éteinte par rapport au format standard du modèle d'observateur. Il comprend le paramètre d'entrée: l'animal nouvellement ajouté, afin que la fonction de notification puisse passer la référence animale nouvellement ajoutée à l'auditeur. Utilisez la fonction foreach de l'API Streams pour traverser les écouteurs et exécuter la fonction Theonanimaladded sur chaque écouteur.
Dans la fonction Addanimal, l'objet animal et l'écoute nouvellement ajouté sont ajoutés à la liste correspondante. Si la complexité du processus de notification n'est pas prise en compte, cette logique doit être incluse dans une méthode d'appel pratique. Il vous suffit de passer dans une référence à l'objet animal nouvellement ajouté. C'est pourquoi l'implémentation logique de l'auditeur de notification est encapsulée dans la fonction NotifyanimalAddedDisteners, qui est également mentionnée dans l'implémentation d'addanimal.
En plus des problèmes logiques de notification des fonctions, il est nécessaire de souligner la question controversée sur la visibilité de la notification des fonctions. Dans le modèle d'observateur classique, comme GOF l'a dit à la page 301 des modèles de conception du livre, la fonction de notification est publique, mais bien que utilisée dans le modèle classique, cela ne signifie pas qu'il doit être public. La sélection de la visibilité doit être basée sur l'application. Par exemple, dans l'exemple du zoo de cet article, la fonction de notification est de type protégé et ne nécessite pas que chaque objet lance une notification d'un observateur enregistré. Il suffit de garantir que l'objet peut hériter de la fonction de la classe parent. Bien sûr, ce n'est pas exactement le cas. Il est nécessaire de déterminer quelles classes peuvent activer la fonction de notification, puis de déterminer la visibilité de la fonction.
Ensuite, vous devez implémenter la classe PrintNameAnimalAddedListener. Cette classe utilise la méthode System.out.println pour sortir le nom du nouvel animal. Le code spécifique est le suivant:
classe publique PrintNameanImalAddedListener implémente AnimalAddedListener {@OverridePublic void updateanimalAdded (animal animal) {// imprime le nom de l'animal de nouveau ajouté.out.println ("ajout d'un nouvel animal avec nom '" + animal.getname () + "");}}}Enfin, nous devons implémenter la fonction principale qui pilote l'application:
classe publique Main {public static void Main (String [] args) {// Créer le zoo pour stocker Animalzoo Zoo = new Zoo (); // Enregistrer un auditeur à notifier lorsqu'un animal est ajoutézoo.RegisteranimalAddeDistener (New PrintnameanImalAddedListener (); // Ajouter un animal NOtify the Enregistrederszoo.addanimal (new Animal ("tigre"));}}La fonction principale crée simplement un objet zoo, enregistre un auditeur qui sortira le nom de l'animal et crée un nouvel objet animal pour déclencher l'auditeur enregistré. La sortie finale est:
Ajout d'un nouvel animal avec nom «tigre»
Écouteur ajouté
Les avantages du mode observateur sont entièrement affichés lorsque l'auditeur est rétabli et ajouté au sujet. Par exemple, si vous souhaitez ajouter un auditeur qui calcule le nombre total d'animaux dans un zoo, il vous suffit de créer une classe d'écoute spécifique et de l'enregistrer auprès de la classe de zoo sans aucune modification de la classe de zoo. L'ajout du code de comptage de l'écoute compteuranImalAddedDistener est le suivant:
classe publique CountinganImalAddedListener implémente AnimalAddedListener {private static int animauxaddedcount = 0; @OverridePublic void updateanimaladded (animal animal) {// incrément le nombre d'animaux.La fonction principale modifiée est la suivante:
classe publique Main {public static void Main (String [] args) {// Créer le zoo pour stocker Animalzoo Zoo = new zoo (); // enregistrer les auditeurs à notifier lorsqu'un animal est ajoutézoo.RegisteranImalAddedDistener (New PrintnameanImalAddededListener ()); zoo.registerMalAddedenener (New CountinganImalAdDistener (); notifier les auditerszoo.Addanimal enregistrés (nouvel animal ("tigre")); zoo.addanimal (nouvel animal ("lion")); zoo.addanimal (nouvel animal ("bear"));}}Le résultat de la sortie est:
Ajout d'un nouvel animal avec nom `` Tiger '' Total Animals Ajouté: 1 Ajout d'un nouvel animal avec nom `` Lion '' Total Animaux ajoutés: 2 Ajout d'un nouvel animal avec nom `` Bear '' Total Animals Ajouté: 3
L'utilisateur peut créer n'importe quel écouteur si vous ne modifiez que le code d'enregistrement de l'écoute. Cette évolutivité est principalement due au sujet associé à l'interface d'observateur, plutôt que directement associée au bétono-serveur. Tant que l'interface n'est pas modifiée, il n'est pas nécessaire de modifier le sujet de l'interface.
Cours internes anonymes, fonctions lambda et enregistrement de l'auditeur
Une amélioration majeure de Java 8 est l'ajout de caractéristiques fonctionnelles, telles que l'ajout de fonctions lambda. Avant d'introduire la fonction lambda, Java a fourni des fonctions similaires via des classes internes anonymes, qui sont toujours utilisées dans de nombreuses applications existantes. En mode observateur, un nouvel écouteur peut être créé à tout moment sans créer une classe d'observateurs spécifique. Par exemple, la classe PrintNameAnimalAddedDistener peut être implémentée dans la fonction principale avec une classe interne anonyme. Le code d'implémentation spécifique est le suivant:
classe publique Main {public static void Main (String [] args) {// Créer le zoo pour stocker Animalzoo Zoo = new Zoo (); // enregistrer les auditeurs à notifier lorsqu'un animal est ajoutézoo.RegisteranimalAddeDistener (New AnimalAddedListener () {@OverRidePublic Void a ajouté UpdateAnimalAdded (Animal Animal AnimalSystem.out.println ("Ajout d'un nouvel animal avec nom '" + animal.getName () + "'");}}); // Ajouter un animal en avertissant les auditerszoo enregistrés.De même, les fonctions de lambda peuvent également être utilisées pour effectuer ces tâches:
classe publique Main {public static void main (String [] args) {// Créer le zoo pour stocker Animalzoo Zoo = new zoo (); // enregistrer les auditeurs à notifier lorsqu'un animal est ajoutézoo.RegisteranimalAddedListener ((Animal) -> System.out.println ("Ajout d'un nouvel animal avec un animal. auditerszoo.addanimal (nouvel animal ("tigre"));}}Il convient de noter que la fonction lambda ne convient qu'aux situations où il n'y a qu'une seule fonction dans l'interface de l'écoute. Bien que cette exigence semble stricte, de nombreux auditeurs sont en fait des fonctions uniques, comme le AnimalAddedListener dans l'exemple. Si l'interface dispose de plusieurs fonctions, vous pouvez choisir d'utiliser des classes intérieures anonymes.
Il y a un tel problème avec l'enregistrement implicite de l'auditeur créé: puisque l'objet est créé dans le cadre de l'appel d'enregistrement, il est impossible de stocker une référence à un écouteur spécifique. Cela signifie que les auditeurs enregistrés via des fonctions lambda ou des classes internes anonymes ne peuvent pas être révoquées car les fonctions de révocation nécessitent une référence à l'auditeur enregistré. Un moyen facile de résoudre ce problème consiste à renvoyer une référence à l'auditeur enregistré dans la fonction RegisteranimalAddedListener. De cette façon, vous pouvez désinscrire l'auditeur créé avec des fonctions lambda ou des classes internes anonymes. Le code de méthode amélioré est le suivant:
public AnimalAddedListener RegisteranImalAddedListener (AnimalAddedListener Écouteur) {// Ajoutez l'auditeur à la liste des auditeurs enregistrés pour les lishes.liseners.add (auditeur); retourner l'auditeur;}Le code client pour l'interaction de fonction redessinée est le suivant:
classe publique Main {public static void Main (String [] args) {// Créer le zoo pour stocker Animalzoo Zoo = new Zoo (); // Enregistrer les auditeurs à notifier lorsqu'un animal est ajouté AndestanImalAddedener auditeur = zoo.gisteranimaladdedListener ((Animal) -> System.out.println ("Ajout d'un nouvel animal avec nom" + animal.get.get.) "'")); // Ajouter un animal avertissant les auditerszoo.Addanimal enregistrés (nouvel animal ("tigre")); // non regroupés The audinerzoo.unregisteranimaladdedener (auditeur); // ajouter un autre animal, qui n'imprimera pas le nom, puisque l'écouteur // a été auparavant non congérédzoo.addanimalal ("" ");}}.La sortie du résultat pour le moment ne s'ajoute qu'à un nouvel animal avec le nom «Tiger», car l'auditeur a été annulé avant l'ajout du deuxième animal:
Ajout d'un nouvel animal avec nom «tigre»
Si une solution plus complexe est adoptée, la fonction de registre peut également renvoyer la classe de récepteur afin que l'auditeur non enregistré soit appelé, par exemple:
classe publique AnimalAddedListeNerreceipt {Écouteur privé AnimalAddedListener; public AnimalAddedListeRecePeipt (AnimalAddedListener Écouteur) {this.Listener = auditeur;} public final AnimalAddedListener GetListener () {return this.Listener;}}Le réception sera utilisé car la valeur de retour de la fonction d'enregistrement et les paramètres d'entrée de la fonction d'enregistrement sont annulés. Pour le moment, l'implémentation du zoo est la suivante:
classe publique ZoousingReceipt {// ... Attributs et constructeurs existants ... public AnimalAddededListeNerreceipt regritsanimalAddeDListener (AnimalAddededListener auditeur) {// Ajoutez l'auditeur à la liste des écouteurs inscrits (écouteur); (AnimalAddedListeNerreceipt Reception) {// Supprimez l'auditeur de la liste des auditeurs enregistrésthis.Listeners.Remove (Receipt.getListener ());} // ... Méthode de notification existante ...}Le mécanisme de mise en œuvre de réception décrit ci-dessus permet le stockage d'informations pour appeler à l'auditeur lors de la révocation, c'est-à-dire, si l'algorithme d'enregistrement de révocation dépend de l'état de l'auditeur lorsque le sujet enregistre l'auditeur, ce statut sera enregistré. Si l'enregistrement de révocation ne nécessite qu'une référence à l'auditeur enregistré précédent, la technologie de réception semblera gênante et n'est pas recommandée.
En plus des auditeurs spécifiques particulièrement complexes, la façon la plus courante d'enregistrer les auditeurs est via des fonctions lambda ou via des classes internes anonymes. Bien sûr, il existe des exceptions, c'est-à-dire que la classe qui contient le sujet implémente l'interface d'observateur et enregistre un auditeur qui appelle la cible de référence. Le cas comme indiqué dans le code suivant:
classe publique zoocontainer implémente AnimalAddedListener {private zoo zoo = new zoo (); public zoocontainer () {// enregistrez cet objet en tant qu'auditerThis.zoo.gisteranimalAddedener (this);} public zoo getzoo () {return this animal) {System.out.println ("Animal ajouté avec nom '" + animal.getName () + "'");} public static void Main (String [] args) {// Créer le zoo contenerzoocontainer zoocontainer = new zoocontainer (); // ajouter un animal notifier intérieurement notifié enversezoocontainer.getzooo.). Animal ("tigre"));}}Cette approche ne convient qu'aux cas simples et le code ne semble pas assez professionnel, et il est toujours très populaire auprès des développeurs Java modernes, il est donc nécessaire de comprendre comment cet exemple fonctionne. Étant donné que Zoocontainer implémente l'interface AnimalAddedListener, alors une instance (ou un objet) de zoocontainer peut être enregistrée comme AnimalAddedListener. Dans la classe Zoocontainer, cette référence représente une instance de l'objet actuel, à savoir le zoocontainer, et peut être utilisé comme AnimalAddedListener.
Généralement, toutes les classes de conteneurs ne sont pas nécessaires pour implémenter de telles fonctions, et la classe de conteneurs qui implémente l'interface de l'écoute ne peut appeler la fonction d'enregistrement du sujet, mais simplement passer la référence à la fonction de registre comme objet d'écoute. Dans les chapitres suivants, les FAQ et les solutions pour les environnements multithread seront introduits.
ONEAPM vous fournit des solutions de performance d'application Java de bout en bout. Nous prenons en charge tous les cadres Java et serveurs d'applications courants pour vous aider à découvrir rapidement les goulots d'étranglement du système et à localiser les causes profondes des anomalies. Déploiement à des niveaux minutieux et expérimentez instantanément, la surveillance Java n'a jamais été aussi simple. Pour lire plus d'articles techniques, veuillez visiter le blog technologique officiel d'OneAPM.
Le contenu ci-dessus présente le contenu pertinent de l'utilisation de Java 8 pour implémenter le mode observateur (partie 1). L'article suivant présente la méthode d'utilisation de Java 8 pour implémenter le mode observateur (partie 2). Les amis intéressés continueront d'apprendre, en espérant que cela sera utile à tous!