Wdrażam bota IRC, który odbiera wiadomość i sprawdzam tę wiadomość, aby określić, które funkcje wywołać. Czy jest na to bardziej sprytny sposób? Wygląda na to, że szybko wymknął się spod kontroli po tym, jak wykonałem 20 poleceń.
Być może istnieje lepszy sposób na streszczenie tego?
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
źródło
źródło
Odpowiedzi:
Użyj tabeli wysyłki . To jest tabela zawierająca pary („część wiadomości”,
pointer-to-function
). Dyspozytor będzie więc wyglądał następująco (w pseudo-kodzie):(
equalsIgnoreCase
może być traktowany gdzieś wcześniej jako specjalny przypadek lub jeśli masz wiele z tych testów, z drugą tabelą wysyłkową).Oczywiście to, jak
pointer-to-function
musi wyglądać, zależy od języka programowania. Oto przykład w C lub C ++. W Javie lub C # prawdopodobnie użyjesz do tego celu wyrażeń lambda lub symulujesz „wskaźnik do funkcji” za pomocą wzorca poleceń. Bezpłatna książka online „ Wyższy porządek Perl ” zawiera pełny rozdział na temat tabel wysyłkowych przy użyciu Perla.źródło
equalsIgnoreCase
„teraz gra”, aletoLowerCase().startsWith
dla innych.toLowerCase
działanie na pętlę.Prawdopodobnie zrobiłbym coś takiego:
Następnie możesz sprawić, by każde polecenie zaimplementowało ten interfejs i zwróciło true, gdy pasuje do komunikatu.
źródło
Command
jest bardziej samowystarczalne, jeśli wie, kiedy należy się nazywać. Powoduje to niewielki narzut, jeśli lista poleceń jest ogromna, ale to prawdopodobnie nieistotne.equals
ihashCode
jest taka sama jak ciąg znaków reprezentujący polecenieUżywasz Javy - więc upiększ ją ;-)
Prawdopodobnie zrobiłbym to za pomocą Adnotacji:
Utwórz niestandardową adnotację metody
Dodaj adnotację do wszystkich odpowiednich metod w klasie, np
W swoim konstruktorze użyj Reflections, aby utworzyć HashMap metod ze wszystkich metod z adnotacjami w twojej klasie:
W swojej
onMessage
metodzie po prostu wykonaj pętlę,commandList
próbując dopasować ciąg na każdym z nich i wywołaćmethod.invoke()
tam, gdzie pasuje.źródło
Co się stanie, jeśli zdefiniujesz interfejs, powiedzmy,
IChatBehaviour
która ma wywoływaną jedną metodę,Execute
która przyjmuje amessage
icmd
obiekt:W swoim kodzie implementujesz ten interfejs i definiujesz pożądane zachowania:
I tak dalej.
W swojej głównej klasie masz listę zachowań (
List<IChatBehaviour>
), które implementuje twój bot IRC. Następnie możesz zamienić swojeif
wypowiedzi na takie:Powyższe powinno zmniejszyć ilość posiadanego kodu. Powyższe podejście pozwoliłoby również na dostarczenie dodatkowych zachowań do twojej klasy bota bez modyfikowania samej klasy bota (zgodnie z
Strategy Design Pattern
).Jeśli chcesz, aby zadziałało tylko jedno zachowanie w danym momencie, możesz zmienić sygnaturę
execute
metody, aby uzyskaćtrue
(zachowanie zadziałało) lubfalse
(zachowanie nie zadziałało) i zastąpić powyższą pętlę czymś takim:Powyższe byłoby bardziej żmudne we wdrażaniu i inicjowaniu, ponieważ musisz utworzyć i przekazać wszystkie dodatkowe klasy, jednak powinno to uczynić twojego bota łatwym do rozszerzenia i modyfikowania, ponieważ wszystkie twoje klasy zachowań będą zamknięte i mam nadzieję, że będą od siebie niezależne.
źródło
if
poszło? Tj. Jak zdecydujesz, że zachowanie jest wykonywane dla polecenia?if
część tego zachowania).IChatBehaviour
poradzi sobie z danym poleceniem, ponieważ pozwala ono wywołującemu zrobić z nim więcej, na przykład przetwarzać błędy, jeśli żadne polecenie nie pasuje, chociaż tak naprawdę jest to tylko osobiste preferencje. Jeśli to nie jest potrzebne, nie ma sensu niepotrzebnie komplikować kodu.„Inteligentny” może obejmować (przynajmniej) trzy rzeczy:
Wyższa wydajność
Sugestia tabeli wysyłki (i jej odpowiedników) jest dobra. Taki stół nazywał się w przeszłości „CADET” dla „Nie można dodać, nawet nie próbuje”. Zastanów się jednak nad komentarzem, który pomoże nowemu opiekunowi, jak zarządzać wspomnianą tabelą.
Konserwowalność
„Make it beautiful” nie jest bezczynnym napomnieniem.
i często pomijane ...
Odporność
Korzystanie z funkcji toLowerCase wiąże się z pułapkami polegającymi na tym, że niektóre teksty w niektórych językach muszą podlegać bolesnej restrukturyzacji przy zmianie między magiscule a maleinule. Niestety w przypadku toUpperCase istnieją takie same pułapki. Po prostu bądź świadomy.
źródło
Możesz mieć wszystkie polecenia implementujące ten sam interfejs. Następnie analizator komunikatów może zwrócić odpowiednie polecenie, które wykonasz tylko.
Wygląda jak tylko więcej kodu. Tak, nadal musisz przeanalizować komunikat, aby wiedzieć, które polecenie wykonać, ale teraz jest ono we właściwie zdefiniowanym punkcie. Może być ponownie użyty w innym miejscu. (Być może chcesz wstrzyknąć MessageParser, ale to inna sprawa. Ponadto wzorzec Flyweight może być dobrym pomysłem dla poleceń, w zależności od tego, ile utworzysz.)
źródło
Chciałbym to zrobić:
Ułatwi to zarządzanie. Więcej korzyści, gdy liczba „else if” zbytnio rośnie.
Oczywiście czasami posiadanie tych „jeśli inaczej” nie byłoby dużym problemem. Nie sądzę, że 20 jest tak źle.
źródło