Ich implementiere einen IRC-Bot, der eine Nachricht empfängt, und überprüfe diese Nachricht, um festzustellen, welche Funktionen aufgerufen werden sollen. Gibt es eine klügere Art, dies zu tun? Es scheint, als würde es schnell außer Kontrolle geraten, nachdem ich 20 Befehle erhalten habe.
Vielleicht gibt es einen besseren Weg, dies zu abstrahieren?
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
quelle
quelle
Antworten:
Verwenden Sie eine Versandtabelle . Dies ist eine Tabelle, die Paare enthält ("Nachrichtenteil",
pointer-to-function
). Der Dispatcher sieht dann so aus (in Pseudocode):(
equalsIgnoreCase
Dies kann als Sonderfall behandelt werden, oder wenn Sie viele dieser Tests haben, mit einer zweiten Versandtabelle).Wie
pointer-to-function
das aussehen muss, hängt natürlich von Ihrer Programmiersprache ab. Hier ist ein Beispiel in C oder C ++. In Java oder C # werden Sie wahrscheinlich Lambda-Ausdrücke für diesen Zweck verwenden, oder Sie simulieren "Zeiger auf Funktionen" mithilfe des Befehlsmusters. Das kostenlose Online-Buch " Higher Order Perl " enthält ein vollständiges Kapitel über Versandtabellen mit Perl.quelle
equalsIgnoreCase
er "Jetzt spielen", abertoLowerCase().startsWith
für die anderen.toLowerCase
Betrieb aus der Schleife.Ich würde wahrscheinlich so etwas machen:
Dann kann jeder Befehl diese Schnittstelle implementieren und true zurückgeben, wenn sie mit der Nachricht übereinstimmt.
quelle
Command
da sie in sich geschlossener ist, wenn sie weiß, wann sie selbst aufgerufen werden muss. Wenn die Liste der Befehle sehr umfangreich ist, entsteht ein geringer Overhead, der jedoch wahrscheinlich vernachlässigbar ist.equals
undhashCode
der Zeichenfolge entspricht, die den Befehl darstelltDu benutzt Java - also mach es schön ;-)
Ich würde dies wahrscheinlich mit Anmerkungen tun:
Erstellen Sie eine benutzerdefinierte Methodenanmerkung
Fügen Sie die Annotation zu allen relevanten Methoden in der Klasse hinzu, z
Verwenden Sie in Ihrem Konstruktor Reflections, um eine HashMap von Methoden aus allen mit Annotationen versehenen Methoden in Ihrer Klasse zu erstellen:
Machen Sie in Ihrer
onMessage
Methode einfach eine Schleife,commandList
indem Sie versuchen, den String für jeden einzelnen zuzuordnen, und rufenmethod.invoke()
Sie auf , wo er passt.quelle
Was ist, wenn Sie eine Schnittstelle definieren, z. B.
IChatBehaviour
welche Methode aufgerufen wird und ein ObjektExecute
aufnimmt :message
cmd
In Ihrem Code implementieren Sie dann diese Schnittstelle und definieren das gewünschte Verhalten:
Und so weiter für den Rest.
In Ihrer Hauptklasse haben Sie dann eine Liste von Verhaltensweisen (
List<IChatBehaviour>
), die Ihr IRC-Bot implementiert. Sie könnten dann Ihreif
Aussagen durch so etwas ersetzen :Das Obige sollte die Menge des Codes reduzieren, den Sie haben. Der obige Ansatz würde es Ihnen auch ermöglichen, Ihrer Bot-Klasse zusätzliches Verhalten zuzuweisen, ohne die Bot-Klasse selbst (gemäß der
Strategy Design Pattern
) zu ändern .Wenn Sie möchten, dass jeweils nur ein Verhalten ausgelöst wird , können Sie die Signatur der
execute
Methode ändern , um zu ergebentrue
(das Verhalten wurde ausgelöst) oderfalse
(das Verhalten wurde nicht ausgelöst ), und die obige Schleife durch Folgendes ersetzen:Die Implementierung und Initialisierung der oben genannten Aktionen ist umso mühsamer, da Sie alle zusätzlichen Klassen erstellen und übergeben müssen. Sie sollten Ihren Bot jedoch leicht erweiterbar und modifizierbar machen, da alle Ihre Verhaltensklassen gekapselt und hoffentlich unabhängig voneinander sind.
quelle
if
s gegangen? Dh, wie entscheidest du, dass ein Verhalten für einen Befehl ausgeführt wird?if
Teil des Verhaltens fälschlicherweise weggelassen ).IChatBehaviour
Befehl einen bestimmten Befehl verarbeiten kann, da der Aufrufer damit mehr anfangen kann, beispielsweise Fehler verarbeiten kann, wenn kein Befehl übereinstimmt, obwohl dies eigentlich nur eine persönliche Präferenz ist. Wenn das nicht benötigt wird, macht es keinen Sinn, Code unnötig zu komplizieren."Intelligent" kann (mindestens) drei Dinge sein:
Höhere Leistung
Der Vorschlag für die Versandtabelle (und ihre Entsprechungen) ist gut. Ein solcher Tisch hieß in den vergangenen Jahren "CADET" für "Can't Add; Doesn't Even Try". Überlegen Sie sich jedoch einen Kommentar, um einem unerfahrenen Betreuer die Verwaltung dieser Tabelle zu erläutern.
Wartbarkeit
"Make it beautiful" ist keine müßige Ermahnung.
und oft übersehen ...
Elastizität
Die Verwendung von toLowerCase birgt die Gefahr, dass Text in einigen Sprachen beim Wechsel zwischen Magiscule und Miniscule schmerzhaft umstrukturiert werden muss. Leider gibt es für toUpperCase die gleichen Fallstricke. Sei dir nur bewusst.
quelle
Sie könnten alle Befehle dieselbe Schnittstelle implementieren lassen. Dann könnte ein Nachrichtenparser den entsprechenden Befehl zurückgeben, den Sie nur ausführen werden.
Es sieht aus wie nur mehr Code. Ja, Sie müssen die Nachricht noch analysieren, um zu wissen, welcher Befehl ausgeführt werden soll. Jetzt befindet sie sich an einem genau definierten Punkt. Es kann an anderer Stelle wiederverwendet werden. (Möglicherweise möchten Sie den MessageParser einschleusen, aber das ist eine andere Sache. Je nachdem, wie viele Befehle erstellt werden sollen, ist das Flyweight-Muster möglicherweise eine gute Idee für die Befehle.)
quelle
Was ich tun würde, ist Folgendes:
Dies macht dies leichter handhabbar. Mehr Nutzen, wenn die Anzahl der 'else if' zu stark ansteigt.
Natürlich wäre es manchmal kein großes Problem, diese "wenn sonst" zu haben. Ich denke nicht, dass 20 so schlimm ist.
quelle