Использование цикла для вызова различных методов в зависимости от найденной строки

Я использую прослушиватели, предоставленные Pircbotx (https://github.com/pircbotx/pircbotx/wiki/Documentation), чтобы определить, когда команда найдена в чате Twitch, и я пытаюсь использовать другой метод в зависимости от того, какая команда вызывается (формат !command). Используемые классы: Listeners, Command.

Команды хранятся в массиве объектов Command, состоящих из одной строки (имя). Каждый объект Command в конечном итоге будет использовать свой собственный метод, который будет определен в классе Command. Объект Listeners при создании немедленно поместит каждый элемент массива в хеш-таблицу (команды).

Когда прослушиватели обнаруживают сообщение, оно сохраняется с использованием локальной строковой переменной (msg). Когда это происходит, цикл перебирает массив объектов Command, а затем.... предполагается, что вызывается метод, соответствующий этому конкретному объекту, в данном случае Command.addDeath(). Вот где я застрял.

Раньше я использовал кучу операторов if для своих слушателей, но когда есть куча команд, все становится очень, очень запутанным. Заранее извиняюсь, если форматирование в моем блоке кода странное, я новичок в использовании Stackverflow, и я также новичок в Java, который учится по ходу дела. После повторного просмотра кода может показаться, что мне на самом деле не нужна хеш-таблица, но я оставлю ее там на тот случай, если у вас, ребята, появятся лучшие идеи, что с ними делать.

      public class Listeners {

String name;
String message;    
private static MessageEvent event;
Command [] commandNames = {new Command("!clearchat", new Command("!addDeath")}; 
Hashtable<String, Command> commands = new Hashtable<String, Command>();

 public Listeners() {
    for (int i = 0; i < commandNames.length; i++) {
        commands.put(commandNames[i].name, new Command(commandNames[i].name));
    }   
    
    if (event.getMessage() != null) {
        String msg = event.getMessage();
        for (int x = 0; x < commandNames.length; x++ ) {
            if (msg.startsWith(commandNames[x].name)) {
                // call Command method here 
            }
        }
    }   
}

А вот и класс Command:

      public class Command {

String name;

public Command(String name) {
        this.name = name;
}

public static void addDeath() {
    DeathCounter.addDeath();
    Listeners.sendMessage("Death Counter: " + DeathCounter.getDeaths());
} 
}

1 ответ

Вы можете использовать интерфейс для своих команд:

      public interface Command {
    public abstract void execute();
}

Затем ваши команды реализуют интерфейс:

      public class DeathCommand implements Command {
    @Override
    public void execute() {
        DeathCounter.addDeath();
        Listeners.sendMessage("Death Counter: " + DeathCounter.getDeaths());
    }
}

В своем классе слушателя сопоставьте строки команд с экземплярами соответствующей команды:

      public class Listeners {

    String name;
    String message;
    private static MessageEvent event;
    static Map<String, Command> commands = new HashMap<>();

    static {
        commands.put("!addDeath", new DeathCommand());
    }

    public Listeners() {
        if (event.getMessage() != null) {
            String msg = event.getMessage();
            Optional<String> key = commands.keySet().stream().filter(k -> msg.startsWith(k)).findFirst();
            key.ifPresent(s -> commands.get(s).execute());
        }
    }
}

Я заменил вашу Hashtable на HashMap, которая во многих отношениях лучше (но используется так же).

Я немного скептически отношусь к использованию карты в качестве статического члена, но, поскольку я не знаком с вашим вариантом использования, я оставлю этот бит как есть.

Редактировать

Все классы Java имеют конструктор по умолчанию (без параметров), если только вы не напишете свой собственный конструктор (вам придется написать конструктор по умолчанию самостоятельно, если он вам все еще нужен). У интерфейсов нет конструкторов, поскольку они не могут быть созданы напрямую. Они просто определяют методы, которые должны быть у реализующих классов. Это позволяет вам иметь ссылки (именованные поля/переменные) типа интерфейса и иметь возможность вызывать методы, не зная или не зная, какая это реализация.

Пример:

      Command com = new DeathCommand();
com.execute(); // <- this will run the execute() code in the DeathCommand class
Command com2 = new SomeOtherCommand();
com2.execute(); // <- this will run the execute() code in the SomeOtherCommand class

Приведенный выше код для команды завершен. Больше ничего нет. Что касается DeathCommand и других реализаций, вам нужно будет добавить необходимый код.

Каждый класс и интерфейс находятся в собственном файле с именем типа:

      Command.java
DeathCommand.java

Что касается HashTable против HashMap. Я должен был сказать, что лучше использовать Map и его реализации. Если вам нужна безопасность потоков, используйте ConcurrentHashMap, как указал agilob, поскольку обычный HashMap не является потокобезопасным.

Другие вопросы по тегам