Lorsque je commence à concevoir une interface, je rencontre toujours le problème que les gestionnaires d'événements sont directement attachés aux boutons, ce qui limite la flexibilité de l'interaction des composants. Le problème est que les boutons standards ne peuvent offrir aucun autre comportement. Ce dont j'ai besoin, c'est d'une isolation logique et d'une gestion dynamique des actions, qui ne sont pas disponibles lors de l'utilisation de boutons standard « prêts à l'emploi ». Dans cet article, je proposerai une solution sur la façon d'adapter les boutons à l'aide des composants Web et du modèle « Commande », ouvrant de nouvelles possibilités pour des interfaces plus flexibles et évolutives.
Habituellement, lorsque nous pensons à un bouton, nous le percevons comme un élément de contrôle graphique qui fournit un moyen simple de déclencher un événement, une action ou un changement dans l'état de l'interface. Il s'agit d'une définition très simple et pratique, qui correspond à notre compréhension quotidienne des éléments de l'interface utilisateur dans les applications Web.
Cependant, lorsque l'on rencontre un bouton dans le contexte du développement Web, lorsque HTML et JavaScript sont impliqués, la première chose qui nous vient à l'esprit est la balise standard, qui est l'outil le plus couramment utilisé pour créer des boutons sur des pages Web. Cette balise ressemble généralement à ceci :
<button onclick="myFunction()">Click me</button>
Mais si l'on y réfléchit, cette balise, bien que servant de bouton, ne reflète pas pleinement tous les aspects et fonctions possibles qu'un bouton peut remplir dans le contexte plus large de l'interaction de l'utilisateur avec une interface.
En examinant de plus près la définition d'un bouton, on remarquera peut-être qu'elle ne fournit aucune information sur l'apparence du bouton, son comportement ou la façon dont il doit déclencher une action. Dans ce contexte, on ne comprend pas clairement ce que l’on entend par « un moyen simple » de déclencher une action, ni comment s’établit la connexion entre le bouton et l’action. Nous ne voyons que la structure de base d'un bouton qui, lorsqu'on clique dessus, appelle une méthode. Mais en réalité, cette simplicité cache un éventail de possibilités et d’approches beaucoup plus large. Et alors, la question se pose : peut-être que le bouton est plus que la simple balise que nous voyons dans l'exemple ci-dessus ?
Approchons le concept de bouton d'un point de vue plus philosophique, en approfondissant son essence et sa fonction. Que représente réellement un bouton ? Si l’on considère l’essence d’une cruche comme son vide, l’essence d’un bouton se retrouve dans sa capacité à initier une action. Un bouton n’est pas seulement un élément de l’interface utilisateur ; c'est un mécanisme qui déclenche un processus spécifique qui existe déjà dans le contexte de l'application. L’action à effectuer se produit au sein de l’application, dans le contexte de l’ensemble du système, mais le lancement de cette action – son démarrage – est la fonction du bouton. On voit donc que le bouton sert en quelque sorte de déclencheur, lançant une action dans un contexte système externe plus large.
Lorsqu'un utilisateur clique sur un bouton, il s'attend à ce que ce clic conduise à une action spécifique. Par conséquent, le bouton se voit attribuer la responsabilité d’initier cette action. Autrement dit, le bouton devient le lien entre l’utilisateur et les actions qui doivent suivre. Cependant, il est important de noter que la méthode ou la fonction qui exécute réellement l’action ne doit pas savoir que c’est le bouton qui l’a déclenchée. Cette distinction entre ce qui initie l'action et ce qui l'exécute est un aspect crucial qui nous permet de maintenir flexibilité et facilité d'interaction dans des systèmes plus complexes.
Lorsque le bouton réalise directement l'action ou lorsque la méthode mettant en œuvre l'action dépend du bouton lui-même, on a affaire à un système assez complexe et interdépendant. Si l’on souhaite simplifier un tel système, il est nécessaire de le décomposer en parties plus simples et indépendantes. Et nous concluons ici que simplifier le processus d’initiation d’une action implique avant tout de séparer le processus d’initiation de l’action elle-même. Et puisque dans le contexte de JavaScript, l'initiateur est souvent appelé événement, nous parlons spécifiquement de séparer l'événement en tant qu'initiateur de la logique qui exécute l'action.
Pourquoi est-il important de séparer l'événement du gestionnaire ?
Tout d'abord, séparer les événements des gestionnaires améliore considérablement la lisibilité du code et favorise la création de solutions plus modulaires. Lorsque la logique du bouton et son gestionnaire sont étroitement liés, ou, pire encore, lorsque le gestionnaire est une fonction anonyme, le code devient extrêmement difficile à lire et à analyser. Cela peut entraîner des problèmes lors de la maintenance et de la mise à jour du projet, car comprendre ce que fait réellement le bouton et quelles modifications sont nécessaires devient une tâche difficile. En revanche, lorsque le gestionnaire est extrait dans une fonction distincte et bien nommée qui reflète clairement l'action effectuée, la structure du code devient plus transparente. Le développeur comprend immédiatement ce qui se passe lorsqu'on clique sur le bouton, et peut plus facilement modifier le comportement de l'élément sans avoir besoin de se plonger dans le reste de la logique. Ainsi, la séparation simplifie à la fois la lecture et la modification du code.
Deuxièmement, séparer la logique des événements et le gestionnaire ouvre des opportunités de réutilisation des gestionnaires dans différentes parties de l'application. Lorsque le gestionnaire est placé dans sa propre fonction, il peut être appliqué non seulement à un bouton, mais à de nombreux autres boutons ayant un comportement similaire. Par exemple, plusieurs boutons effectuant la même action peuvent utiliser le même gestionnaire, ce qui réduit la duplication de code et augmente l'efficacité. De plus, le gestionnaire peut être déclenché non seulement via le bouton mais également par d'autres moyens, tels que des appels programmatiques ou des actions initiées par d'autres parties de l'interface. Cela élargit considérablement les fonctionnalités de votre application, augmentant sa flexibilité et son évolutivité.
Troisièmement, séparer les événements et les gestionnaires permet plus de flexibilité dans les boutons eux-mêmes. Si le comportement du bouton est désormais déterminé non pas au sein du bouton lui-même, mais via un gestionnaire distinct, il devient facile de modifier ses actions ou de les réaffecter en fonction de la situation. Ceci est particulièrement important dans les projets dotés d'interfaces dynamiques, où le comportement des éléments peut changer en réponse aux actions de l'utilisateur ou aux changements dans l'état de l'application. Cette approche permet à l'interface de s'adapter facilement à l'évolution des exigences sans perturber la structure globale du code.
Quatrièmement, la séparation des événements et des gestionnaires est cruciale pour la testabilité, en particulier dans les grands projets. Lorsque les gestionnaires d'événements sont extraits dans des fonctions distinctes, les tester devient beaucoup plus facile, car ils peuvent être testés indépendamment de l'interface. Vous pouvez isoler le gestionnaire et tester son fonctionnement avec divers paramètres, sans vous soucier de l'interaction avec d'autres parties de l'interface. Cela facilite les tests, améliore la fiabilité et la stabilité de l'application tout en minimisant le risque d'erreurs.
La séparation de l'événement de bouton et du gestionnaire est une étape clé vers une architecture de code plus propre, plus flexible et maintenable. Ceci est particulièrement important dans les projets complexes, où les interactions entre les éléments d’interface deviennent plus complexes et interdépendantes. Cette approche contribue à améliorer la stabilité du système, facilite l'extension et la modification de l'application et réduit le risque d'erreurs survenant lors de ces modifications.
Un exemple de séparation de l'événement d'un bouton de son gestionnaire peut être trouvé dans n'importe quel guide du débutant.
<button onclick="myFunction()">Click me</button>
Si les boutons pouvaient transmettre non seulement le contexte d'une interaction mais également l'intention explicite de l'utilisateur au sein de l'événement, cela simplifierait considérablement l'architecture. Les gestionnaires pourraient se concentrer sur l'exécution de tâches plutôt que sur l'attribution d'une logique aux événements.
Cela souligne la nécessité de s'éloigner de la compréhension traditionnelle d'un bouton en tant que simple initiateur d'événement. Au lieu de cela, il suggère d'adopter un modèle plus avancé dans lequel le bouton agit comme un pont entre l'intention de l'utilisateur et la logique de l'application.
Pour créer un modèle plus avancé pour la gestion des événements, nous pouvons exploiter le modèle Command, qui permet de lier les événements à la logique de l'application à un niveau d'abstraction plus élevé. Ceci peut être réalisé en introduisant une couche qui transforme les événements ordinaires en commandes telles que saveDocument ou deleteItem. En utilisant cette approche, un événement devient plus qu'un simple signal indiquant que quelque chose s'est produit - il se transforme en ce qu'il est censé être : l'initiateur d'une action, comme indiqué plus haut dans l'article.
Mais cela soulève une question : pourquoi les développeurs d’événements JavaScript n’ont-ils pas implémenté le modèle Command dès le départ ? Pourquoi les événements ont-ils été conçus comme ils le sont aujourd’hui ? Et pourquoi les événements étaient-ils nécessaires en premier lieu ?
Lorsque HTML et les technologies associées telles que DOM et JavaScript ont été initialement développées, leur objectif principal était de créer une structure simple pour les documents hypertextes qui permettrait aux utilisateurs d'interagir avec les pages Web. À cette époque, l’interaction des utilisateurs était considérablement limitée et le modèle de gestion des événements n’était pas conçu pour s’adapter à des mécanismes complexes tels que le modèle Command. Il est essentiel de comprendre que les débuts du Web ont été développés pour simplifier la création et la gestion de contenu, et non pour fournir des outils sophistiqués pour une logique complexe côté client.
Dans les années 1990, lorsque le HTML et le Web ont été créés, leur objectif était de fournir un moyen simple de présenter des documents hypertextes avec une interaction minimale de l'utilisateur. L'objectif principal était de soumettre des données aux serveurs plutôt que d'exécuter une logique complexe dans le navigateur. Les boutons et les formulaires étaient principalement utilisés pour envoyer des données et non pour lancer des processus côté client. Tous les calculs et le traitement des données étaient gérés sur le serveur, avec des boutons servant d'éléments d'interface déclenchant la soumission des données au backend.
Le modèle de commande nécessite une structure plus sophistiquée qui implique une séparation claire entre l'interface et la logique de traitement, ainsi qu'un mécanisme pour spécifier l'action exacte à exécuter. Ces idées ne sont devenues pertinentes que plus tard, à mesure que le besoin d’interfaces dynamiques et d’une plus grande interactivité dans les applications Web s’est accru. Les interactions dynamiques et complexes, telles que le déclenchement d'une logique côté client via des événements, ont nécessité de nouvelles approches, notamment l'adoption du modèle Command.
Le modèle de commande peut-il être appliqué aux boutons aujourd’hui ? Oui, c'est possible. Bien que les boutons HTML standard ne prennent pas directement en charge le modèle de commande, les technologies modernes telles que les événements personnalisés nous permettent de créer des mécanismes similaires. Par exemple, nous avons déjà exploré comment la propriété detail peut être utilisée pour transmettre des données supplémentaires avec des événements.
Cependant, cette approche n'est toujours pas idéale, car elle nécessite de créer des implémentations distinctes pour chaque bouton de l'interface. Cela ajoute une complexité supplémentaire et rend la mise à l'échelle de tels systèmes plus difficile.
Exploiter les composants Web pour moderniser les boutons et les aligner sur le modèle de commande est une approche prometteuse qui peut améliorer considérablement à la fois l'architecture et la flexibilité des interactions dans votre projet. Les composants Web fournissent des outils puissants pour créer des éléments d'interface réutilisables qui peuvent être intégrés de manière transparente dans diverses parties d'une application.
Au lieu d'écrire des gestionnaires distincts pour chaque bouton, vous pouvez créer un composant unifié qui agit comme un bouton avec la possibilité supplémentaire de transmettre une commande. Cette approche améliore non seulement la structure du code mais améliore également sa lisibilité et sa maintenabilité.
Voici un exemple de base d'un tel composant :
<button onclick="myFunction()">Click me</button>
Lorsqu'un composant de bouton transmet un identifiant de commande et des paramètres potentiellement supplémentaires, il établit les bases d'une architecture plus avancée. Dans cette configuration, le composant contenant le bouton et s'abonnant à ses événements agit essentiellement comme un contrôleur qui traite la commande passée via l'événement.
Dans les modèles architecturaux tels que MVC (Model-View-Controller), le contrôleur sert d'intermédiaire entre le modèle, qui représente les données, et la vue, qui constitue l'interface utilisateur. Il reçoit les entrées de l'utilisateur, telles que les clics sur des boutons, et gère les modifications résultantes des données ou de l'état, qui sont ensuite reflétées dans l'interface.
L'utilisation d'un contrôleur au sein d'un composant offre plusieurs avantages clés. Premièrement, il encapsule la logique d’exécution des commandes, évitant ainsi toute complexité inutile au code de l’application principale. Les détails de mise en œuvre restent cachés dans le contrôleur lui-même. Deuxièmement, cette approche améliore la modularité, permettant de réutiliser les boutons simplement en passant différentes commandes et paramètres. Cela réduit également le couplage au sein de l'application, car les modifications apportées à la logique de gestion des commandes nécessitent des modifications uniquement au sein du contrôleur, sans affecter les autres parties du système. Enfin, les contrôleurs offrent une flexibilité importante. Ils peuvent gérer à la fois des commandes simples, telles que « enregistrer » ou « supprimer », et des actions plus complexes, tandis que le composant bouton reste simple et concentré uniquement sur son rôle principal.
Cette architecture facilite une séparation nette des préoccupations. Le composant bouton émet un événement personnalisé qui inclut la commande et ses données pertinentes, tandis que le composant parent, agissant en tant que contrôleur, écoute cet événement. Le contrôleur traite la commande, interagit avec le modèle de données si nécessaire et met à jour l'interface utilisateur en conséquence. Cette approche aboutit à une architecture plus propre et plus évolutive, plus facile à étendre et à maintenir, tout en gardant les composants des boutons réutilisables et indépendants de la logique qu'ils déclenchent.
En conclusion, l'approche où un bouton déclenche non seulement une action mais transmet également une commande avec les données nécessaires via un événement est un excellent exemple d'application du modèle "Command". Cette méthode améliore considérablement l'organisation de l'interaction de l'interface en séparant la logique d'exécution des commandes des éléments de l'interface, améliorant ainsi la flexibilité et l'évolutivité des applications.
Cependant, une telle approche est encore relativement rare dans la pratique. Au lieu de tirer parti des puissantes capacités des composants Web pour créer des solutions universelles et flexibles, de nombreux développeurs continuent de s'appuyer sur des boutons standard directement liés aux gestionnaires d'événements. Cela est probablement dû à l'habitude et au manque de conscience des avantages de cette approche, conduisant à une utilisation plus conventionnelle des boutons comme simples déclencheurs d'actions.
Déterminé à changer cette situation, j'ai développé la bibliothèque KoiCom, où de nombreux composants ont déjà été adaptés et enrichis. En particulier, les boutons de cette bibliothèque suivent le modèle "Command", transmettant les données et commandes nécessaires via des événements. Cette approche augmente considérablement la modularité, la flexibilité et la maintenabilité, éliminant la logique redondante et simplifiant la gestion des commandes.
Documentation KoiCom
KoiCom github
En fin de compte, j'espère que de telles solutions aideront les développeurs à adopter une approche plus moderne de la conception d'interfaces, rendant les applications plus évolutives et plus faciles à maintenir.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!