Estoy implementando un bot IRC que recibe un mensaje y lo reviso para determinar a qué funciones llamar. ¿Hay alguna forma más inteligente de hacer esto? Parece que rápidamente se saldría de control después de que me dieron 20 comandos.
Tal vez hay una mejor manera de abstraer esto?
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
fuente
fuente
Respuestas:
Use una tabla de despacho . Esta es una tabla que contiene pares ("parte del mensaje",
pointer-to-function
). El despachador se verá así (en pseudocódigo):(
equalsIgnoreCase
se puede manejar como un caso especial en algún lugar antes, o si tiene muchas de esas pruebas, con una segunda tabla de despacho).Por supuesto, lo que
pointer-to-function
debe verse depende de su lenguaje de programación. Aquí hay un ejemplo en C o C ++. En Java o C #, probablemente usará expresiones lambda para ese propósito, o simulará "puntero a funciones" usando el patrón de comando. El libro en línea gratuito " Higher Order Perl " tiene un capítulo completo sobre las tablas de despacho con Perl.fuente
equalsIgnoreCase
para "jugar ahora" perotoLowerCase().startsWith
para los demás.toLowerCase
operación fuera del bucle.Probablemente haría algo como esto:
Luego puede hacer que cada comando implemente esta interfaz y devuelva verdadero cuando coincida con el mensaje.
fuente
Command
es más autónomo si sabe cuándo llamarse a sí mismo. Produce una ligera sobrecarga si la lista de comandos es enorme, pero probablemente sea insignificante.equals
yhashCode
sea igual a la cadena que representa el comandoEstás utilizando Java, así que hazlo hermoso ;-)
Probablemente haría esto usando Anotaciones:
Crear una anotación de método personalizada
Agregue la anotación a todos los métodos relevantes en la clase, por ejemplo
En su constructor, use Reflections para crear un HashMap de métodos a partir de todos los métodos anotados en su clase:
En su
onMessage
Método, solo haga un bucle paracommandList
tratar de hacer coincidir la Cadena en cada uno y llamarmethod.invoke()
donde encaja.fuente
¿Qué pasa si define una interfaz, digamos
IChatBehaviour
cuál tiene un método llamadoExecute
que toma enmessage
uncmd
objeto?En su código, entonces implementa esta interfaz y define los comportamientos que desea:
Y así por el resto.
En su clase principal, tiene una lista de comportamientos (
List<IChatBehaviour>
) que implementa su bot IRC. Entonces podría reemplazar susif
declaraciones con algo como esto:Lo anterior debería reducir la cantidad de código que tiene. El enfoque anterior también le permitiría proporcionar comportamientos adicionales a su clase de bot sin modificar la clase de bot en sí (según el
Strategy Design Pattern
).Si desea que solo se active un comportamiento a la vez, puede cambiar la firma del
execute
método para obtenertrue
(el comportamiento se ha activado) ofalse
(el comportamiento no se activó) y reemplazar el bucle anterior con algo como esto:Lo anterior sería más tedioso de implementar e inicializar ya que necesita crear y pasar todas las clases adicionales, sin embargo, debería hacer que su bot sea fácilmente extensible y modificable ya que todas sus clases de comportamiento serán encapsuladas y, con suerte, independientes entre sí.
fuente
if
s? Es decir, ¿cómo decides que se ejecuta un comportamiento para un comando?if
parte del comportamiento).IChatBehaviour
puede manejar un comando dado, ya que permite que la persona que llama haga más con él, como errores de procesamiento si ningún comando coincide, aunque en realidad es solo una preferencia personal. Si eso no es necesario, entonces no tiene sentido complicar innecesariamente el código."Inteligente" puede ser (al menos) tres cosas:
Mayor rendimiento
La sugerencia de la tabla de despacho (y sus equivalentes) es buena. Tal tabla se llamaba "CADET" en años anteriores para "No se puede agregar; ni siquiera se intenta". Sin embargo, considere un comentario para ayudar a un mantenedor novato sobre cómo administrar dicha tabla.
Mantenibilidad
"Hacerlo hermoso" no es una advertencia ociosa.
y, a menudo pasado por alto ...
Resistencia
El uso de toLowerCase tiene dificultades en el sentido de que algunos textos en algunos idiomas deben sufrir una reestructuración dolorosa al cambiar entre magiscule y miniscule. Desafortunadamente, existen los mismos escollos para toUpperCase. Solo ten en cuenta.
fuente
Puede hacer que todos los comandos implementen la misma interfaz. Luego, un analizador de mensajes podría devolverle el comando apropiado que solo ejecutará.
Parece que solo hay más código. Sí, aún necesita analizar el mensaje para saber qué comando ejecutar, pero ahora está en un punto definido correctamente. Puede ser reutilizado en otro lugar. (Es posible que desee inyectar el MessageParser, pero ese es otro asunto. Además, el patrón Flyweight podría ser una buena idea para los comandos, dependiendo de cuántos espera crear.)
fuente
Lo que haría es esto:
Esto hará que esto sea más manejable. Más beneficio cuando el número de 'más si' crece demasiado.
Por supuesto, a veces tener estos 'si no' no sería un gran problema. No creo que 20 sea tan malo.
fuente