Осуществите рефакторинг код Java

Хорошо угадайте, что этот вопрос смотрит много как:

Что лучший способ состоит в том, чтобы заменить или заменить если.. еще, если.. еще деревья в программах?

считайте этот вопрос ЗАКРЫТЫМ!


Я хотел бы осуществить рефакторинг код, который выглядит примерно так:

String input; // input from client socket.
if (input.equals(x)) {
  doX();
} else if (input.equals(y)) {
  doY();
} else {
  unknown_command();
}

Это - код, который проверяет вход от сокета для выполнения некоторого действия, но мне не нравится if else конструкция, потому что каждый раз новая команда добавляется к серверу (код) новое, если еще должен быть добавлен, который ужасен. Также при удалении команды if else должен быть изменен.

6
задан Community 23 May 2017 в 12:02
поделиться

5 ответов

Соберите эти команды в Map , где Command - это интерфейс с методом execute () .

Map<String, Command> commands = new HashMap<String, Command>();
// Fill it with concrete Command implementations with `x`, `y` and so on as keys.

// Then do:
Command command = commands.get(input);
if (command != null) {
    command.execute();
} else {
    // unknown command.
}

Чтобы продвинуться дальше, вы можете рассмотреть возможность динамического заполнения карты путем сканирования классов, реализующих определенный интерфейс ( Команда в данном случае) или конкретную аннотацию в пути к классам. Google Reflections может очень помочь в этом.

Обновление (из комментариев) Вы также можете рассмотреть возможность объединения ответа Instantsoup с моим ответом. Во время метода buildExecutor () сначала получите команду из Map , и если команда не существует в Map , затем попробуйте загрузить связанный класс и поместите его на карту . Этакая ленивая загрузка. Это более эффективно, чем сканирование всего пути к классам, как в моем ответе, и его создание каждый раз, как в ответе Instantsoup.

8
ответ дан 8 December 2019 в 13:45
поделиться

Построение шаблона Command на основе класса enum может сократить часть стандартного кода. Предположим, что x в input.equals (x) - это «XX», а y в input.equals (y) - это «YY»

enum Commands {
   XX {
     public void execute() { doX(); }        
   },
   YY {
     public void execute() { doY(); }        
   };

   public abstract void execute();
}

String input = ...; // Get it from somewhere

try {
  Commands.valueOf(input).execute();
}
catch(IllegalArgumentException e) {
   unknown_command();
}
2
ответ дан 8 December 2019 в 13:45
поделиться

Как насчет интерфейсов, фабрики и небольшого отражения? Вам все равно придется обрабатывать исключения при неверном вводе, но это всегда нужно делать. С помощью этого метода вы просто добавляете новую реализацию Executor для нового ввода.

public class ExecutorFactory
{
    public static Executor buildExecutor(String input) throws Exception
    {
        Class<Executor> forName = (Class<Executor>) Class.forName(input);
        return (Executor) executorClass.newInstance();
    }
}

public interface Executor
{
    public void execute();
}


public class InputA implements Executor
{
    public void execute()
    {
        // do A stuff
    }
}

public class InputB implements Executor
{
    public void execute()
    {
        // do B stuff
    }
}

Ваш пример кода становится

String input;
ExecutorFactory.buildExecutor(input).execute();
3
ответ дан 8 December 2019 в 13:45
поделиться

Одним из способов может быть интерфейс ICommand, который является общим контрактом для команды, например. :

public interface ICommand {
    /** @param context The command's execution context */
    public void execute(final Object context);
    public String getKeyword();
}

А затем вы можете использовать механизм Java SPI для автоматического обнаружения ваших различных реализаций и зарегистрировать их в Map, а затем сделать knownCommandsMap.get(input).execute(ctx) или что-то подобное.

Это практически позволяет вам отделить ваш сервис от реализации команд, фактически делая их сменными.

Регистрация класса реализации в SPI осуществляется путем добавления файла с полным именем вашего класса ICommand (так, если он находится в пакете dummy, файл будет META-INF/dummy. ICommand в вашем classpath), а затем вы загрузите и зарегистрируете их следующим образом:

final ServiceLoader<ICommand> spi = ServiceLoader.load(ICommand.class);
for(final ICommand commandImpl : spi)
    knownCommandsMap.put(commandImpl.getKeyword(), commandImpl);
3
ответ дан 8 December 2019 в 13:45
поделиться

Вы говорите, что обрабатываете ввод из сокета. Насколько много ввода? Насколько это сложно? Насколько он структурирован?

В зависимости от ответов на эти вопросы вам может быть лучше написать грамматику и позволить генератору синтаксического анализатора (например, ANTLR ) генерировать код обработки ввода.

1
ответ дан 8 December 2019 в 13:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: