Хорошо угадайте, что этот вопрос смотрит много как:
считайте этот вопрос ЗАКРЫТЫМ!
Я хотел бы осуществить рефакторинг код, который выглядит примерно так:
String input; // input from client socket.
if (input.equals(x)) {
doX();
} else if (input.equals(y)) {
doY();
} else {
unknown_command();
}
Это - код, который проверяет вход от сокета для выполнения некоторого действия, но мне не нравится if else
конструкция, потому что каждый раз новая команда добавляется к серверу (код) новое, если еще должен быть добавлен, который ужасен. Также при удалении команды if else
должен быть изменен.
Соберите эти команды в 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.
Построение шаблона 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();
}
Как насчет интерфейсов, фабрики и небольшого отражения? Вам все равно придется обрабатывать исключения при неверном вводе, но это всегда нужно делать. С помощью этого метода вы просто добавляете новую реализацию 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();
Одним из способов может быть интерфейс 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);
Вы говорите, что обрабатываете ввод из сокета. Насколько много ввода? Насколько это сложно? Насколько он структурирован?
В зависимости от ответов на эти вопросы вам может быть лучше написать грамматику и позволить генератору синтаксического анализатора (например, ANTLR ) генерировать код обработки ввода.