Estou implementando um bot de IRC que recebe uma mensagem e estou verificando essa mensagem para determinar quais funções chamar. Existe uma maneira mais inteligente de fazer isso? Parece que rapidamente ficaria fora de controle depois que eu conseguia 20 comandos.
Talvez haja uma maneira melhor de abstrair isso?
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
fonte
fonte
Respostas:
Use uma tabela de expedição . Esta é uma tabela que contém pares ("parte da mensagem"
pointer-to-function
). O expedidor ficará assim (em pseudo código):(
equalsIgnoreCase
pode ser tratado como um caso especial em algum lugar antes, ou se você tiver muitos desses testes, com uma segunda tabela de despacho).Obviamente, o que
pointer-to-function
deve parecer depende da sua linguagem de programação. Aqui está um exemplo em C ou C ++. Em Java ou C #, você provavelmente utilizará expressões lambda para esse fim ou simulará "ponteiro para funções" usando o padrão de comando. O livro on-line gratuito " Higher Order Perl " tem um capítulo completo sobre tabelas de expedição usando Perl.fonte
equalsIgnoreCase
para "agora jogando", mastoLowerCase().startsWith
para os outros.toLowerCase
operação do circuito.Eu provavelmente faria algo assim:
Em seguida, todos os comandos podem implementar essa interface e retornar true quando corresponder à mensagem.
fonte
Command
é mais autocontida se souber quando deve ser chamada de si mesma. Produz uma pequena sobrecarga se a lista de comandos for enorme, mas provavelmente insignificante.equals
ehashCode
ser o mesmo que o string que representa o comandoVocê está usando Java - então faça bonito ;-)
Eu provavelmente faria isso usando anotações:
Criar uma anotação de método personalizada
Adicione a anotação a todos os métodos relevantes da classe, por exemplo
No seu construtor, use o Reflections para criar um HashMap de Métodos a partir de todos os Métodos anotados em sua classe:
No seu
onMessage
Método, basta fazer um loopcommandList
tentando combinar a String em cada uma e chamandomethod.invoke()
onde ela se encaixa.fonte
E se você definir uma interface, diga
IChatBehaviour
qual possui um método chamadoExecute
que recebe ummessage
e umcmd
objeto:No seu código, você implementa essa interface e define os comportamentos que deseja:
E assim por diante.
Na sua classe principal, você tem uma lista de comportamentos (
List<IChatBehaviour>
) implementados pelo seu bot de IRC. Você pode substituir suasif
instruções por algo assim:O acima deve reduzir a quantidade de código que você possui. A abordagem acima também permitirá que você forneça comportamentos adicionais à sua classe de bot sem modificar a própria classe de bot (conforme a
Strategy Design Pattern
).Se você deseja que apenas um comportamento seja acionado a qualquer momento, você pode alterar a assinatura do
execute
método para produzirtrue
(o comportamento foi acionado) oufalse
(o comportamento não foi acionado ) e substituir o loop acima por algo assim:O exemplo acima seria mais tedioso para implementar e inicializar, pois você precisa criar e passar todas as classes extras; no entanto, ele deve tornar seu bot facilmente extensível e modificável, já que todas as suas classes de comportamento serão encapsuladas e esperançosamente independentes uma da outra.
fonte
if
? Ou seja, como você decide que um comportamento é executado para um comando?if
parte no comportamento).IChatBehaviour
pode manipular um determinado comando, pois ele permite que o chamador faça mais com ele, como processar erros se nenhum comando corresponder, embora seja realmente apenas uma preferência pessoal. Se isso não for necessário, não adianta complicar desnecessariamente o código."Inteligente" pode ser (pelo menos) três coisas:
Maior desempenho
A sugestão da Tabela de expedição (e seus equivalentes) é boa. Essa tabela foi chamada de "CADET" nos últimos anos para "Não é possível adicionar; nem sequer tenta". No entanto, considere um comentário para ajudar um mantenedor iniciante sobre como gerenciar a referida tabela.
Manutenção
"Faça bonito" não é uma advertência ociosa.
e, muitas vezes esquecido ...
Resiliência
O uso do toLowerCase apresenta armadilhas, pois alguns textos em alguns idiomas precisam sofrer uma reestruturação dolorosa ao mudar entre magúsculo e minúsculo. Infelizmente, existem as mesmas armadilhas para o toUpperCase. Apenas esteja ciente.
fonte
Todos os comandos podem ser implementados na mesma interface. Em seguida, um analisador de mensagens poderá retornar o comando apropriado que você executará apenas.
Parece apenas mais código. Sim, você ainda precisa analisar a mensagem para saber qual comando executar, mas agora está em um ponto definido corretamente. Pode ser reutilizado em outro lugar. (Você pode injetar o MessageParser, mas isso é outra questão. Além disso, o padrão Flyweight pode ser uma boa idéia para os comandos, dependendo de quantos você espera que seja criado.)
fonte
O que eu faria é o seguinte:
Isso tornará isso mais gerenciável. Mais benefício quando o número de 'else if' cresce demais.
É claro que, às vezes, ter esse 'se mais' não seria um grande problema. Eu não acho que 20 é tão ruim assim.
fonte