J'implémente un bot IRC qui reçoit un message et je vérifie ce message pour déterminer les fonctions à appeler. Existe-t-il une manière plus intelligente de procéder? Il semble que cela deviendrait rapidement incontrôlable après que je me sois levé pour aimer 20 commandes.
Peut-être y a-t-il une meilleure façon d'abstraire cela?
public void onMessage(String channel, String sender, String login, String hostname, String message){
if (message.equalsIgnoreCase(".np")){
// TODO: Use Last.fm API to find the now playing
} else if (message.toLowerCase().startsWith(".register")) {
cmd.registerLastNick(channel, sender, message);
} else if (message.toLowerCase().startsWith("give us a countdown")) {
cmd.countdown(channel, message);
} else if (message.toLowerCase().startsWith("remember am routine")) {
cmd.updateAmRoutine(channel, message, sender);
}
}
java
design
abstraction
Harrison Nguyen
la source
la source
Réponses:
Utilisez une table de répartition . Il s'agit d'un tableau contenant des paires ("partie message",
pointer-to-function
). Le répartiteur ressemblera alors à ceci (en pseudo code):(le
equalsIgnoreCase
peut être traité comme un cas spécial quelque part auparavant, ou si vous avez plusieurs de ces tests, avec une deuxième table de répartition).Bien sûr, ce
pointer-to-function
à quoi cela doit ressembler dépend de votre langage de programmation. Voici un exemple en C ou C ++. En Java ou en C #, vous utiliserez probablement des expressions lambda à cet effet, ou vous simulez des "pointeurs vers des fonctions" en utilisant le modèle de commande. Le livre en ligne gratuit " Higher Order Perl " contient un chapitre complet sur les tables de répartition utilisant Perl.la source
equalsIgnoreCase
pour "jouer maintenant" maistoLowerCase().startsWith
pour les autres.toLowerCase
opération hors de la boucle.Je ferais probablement quelque chose comme ça:
Ensuite, vous pouvez demander à chaque commande d'implémenter cette interface et de renvoyer true lorsqu'elle correspond au message.
la source
Command
est plus autonome si elle sait quand être appelée elle-même. Cela produit une légère surcharge si la liste des commandes est énorme mais c'est probablement négligeable.equals
ethashCode
pour être la même que la chaîne qui représente la commandeVous utilisez Java - alors rendez-le beau ;-)
Je le ferais probablement en utilisant des annotations:
Créer une annotation de méthode personnalisée
Ajoutez l'annotation à toutes les méthodes pertinentes de la classe, par exemple
Dans votre constructeur, utilisez Reflections pour créer un HashMap de méthodes à partir de toutes les méthodes annotées de votre classe:
Dans votre
onMessage
méthode, il suffit de faire une boucle pourcommandList
essayer de faire correspondre la chaîne sur chacun et d'appelermethod.invoke()
où elle se situe.la source
Que se passe-t-il si vous définissez une interface, disons
IChatBehaviour
laquelle a une méthode appeléeExecute
qui prend unmessage
et uncmd
objet:Dans votre code, vous implémentez ensuite cette interface et définissez les comportements que vous souhaitez:
Et ainsi de suite pour le reste.
Dans votre classe principale, vous avez alors une liste de comportements (
List<IChatBehaviour>
) que votre bot IRC implémente. Vous pouvez ensuite remplacer vosif
déclarations par quelque chose comme ceci:Ce qui précède devrait réduire la quantité de code dont vous disposez. L'approche ci-dessus vous permettrait également de fournir des comportements supplémentaires à votre classe de bot sans modifier la classe de bot elle-même (selon le
Strategy Design Pattern
).Si vous souhaitez qu'un seul comportement se déclenche à la fois, vous pouvez modifier la signature de la
execute
méthode pour produiretrue
(le comportement s'est déclenché) oufalse
(le comportement ne s'est pas déclenché) et remplacer la boucle ci-dessus par quelque chose comme ceci:Ce qui précède serait plus fastidieux à mettre en œuvre et à initialiser car vous devez créer et passer toutes les classes supplémentaires, cependant, cela devrait rendre votre bot facilement extensible et modifiable car toutes vos classes de comportement seront encapsulées et, espérons-le, indépendantes les unes des autres.
la source
if
passés les s? C'est-à-dire, comment décidez-vous qu'un comportement est exécuté pour une commande?if
partie du comportement).IChatBehaviour
peut gérer une commande donnée, car elle permet à l'appelant d'en faire plus, comme traiter les erreurs si aucune commande ne correspond, bien que ce ne soit vraiment qu'une préférence personnelle. Si cela n'est pas nécessaire, inutile de compliquer inutilement le code."Intelligent" peut être (au moins) trois choses:
Des performances supérieures
La suggestion du tableau de répartition (et de ses équivalents) est bonne. Une telle table s'appelait "CADET" dans les années passées pour "Impossible d'ajouter; N'essaye même pas." Cependant, considérez un commentaire pour aider un mainteneur novice à savoir comment gérer ladite table.
Maintenabilité
"Make it beautiful" n'est pas un avertissement inutile.
et, souvent négligé ...
Élasticité
L'utilisation de toLowerCase présente des pièges dans la mesure où certains textes dans certaines langues doivent subir une restructuration douloureuse lors du passage entre magiscule et minuscule. Malheureusement, les mêmes pièges existent pour toUpperCase. Soyez juste conscient.
la source
Vous pourriez avoir toutes les commandes implémenter la même interface. Un analyseur de messages pourrait alors vous renvoyer la commande appropriée que vous n'exécuterez que.
Il ressemble à juste plus de code. Oui, vous devez toujours analyser le message afin de savoir quelle commande exécuter, mais maintenant elle est à un point correctement défini. Il peut être réutilisé ailleurs. (Vous voudrez peut-être injecter le MessageParser mais c'est une autre question. En outre, le modèle Flyweight peut être une bonne idée pour les commandes, selon le nombre que vous prévoyez d'être créé.)
la source
Ce que je ferais c'est ceci:
Cela rendra cela plus gérable. Plus d'avantages lorsque le nombre de «sinon si» augmente trop.
Bien sûr, avoir parfois ces «sinon» ne serait pas un gros problème. Je ne pense pas que 20 est si mauvais.
la source