Улучшение разборчивости условных выражений
Я строю HTTP-сервер для моего устройства Android.
Я использую много операторов IF-ELSE для обработки разных запросов.
Поскольку я буду делиться своим кодом с другими людьми для дальнейшего использования, мне придется сделать его как можно более разборчивым. Прямо сейчас я даже не могу читать свой код с легкостью.
Я думаю, что проблема заключается в использовании большого количества операторов IF-ELSE в одном классе. Например.
if(purpose.equals("readProfile"){
.....
}
else if(purpose.equals("writeProfile"){
.....
}
....
Я попытался классифицировать их в категории и упорядочил условия в соответствии с их категорией. Но не много легенда была улучшена. Затем я попытался написать короткие комментарии перед каждым условием. Но это сделало еще больше беспорядка.
Что можно сделать, чтобы повысить удобочитаемость условных выражений?
5 ответов
Как заявил Luiggi Mendoza, это продолжение предыдущего вопроса...
Если вы используете Java 7, вы можете использовать оператор switch-case для строк
//month is a String
switch (month.toLowerCase()) {
case "january":
monthNumber = 1;
break;
//partsleft out for sake of brevity ..
default:
monthNumber = 0;
break;
}
(выдержка из Oracle Java Tutorials, на которую есть ссылка выше.)
Рефакторинг
Тем не менее, этот огромный if-else является лишь частью проблемы. Поскольку эта структура со временем растет, я рекомендую провести тщательный рефакторинг, и использование того, что мне кажется, является паттерном стратегии. Вам следует:
Сформулируйте интерфейс, который охватывает границы для всех вариантов использования:
interface MyStrategy {
void execute(MyInputContext input, MyOutputContext output);
}
(использование метода void с MyInputContext и MyOutputContext - это всего лишь один из подходов, это всего лишь пример, но для обработки запросов, имеющих ответы, это имеет смысл, как и работа сервлетов)
Измените содержимое большого оператора IF-ELSE на экземпляры этого интерфейса (это будут стратегии):
//VERY simplified...
class ReadProfileStrategy implements MyStrategy {
void execute(MyInputContext input, MyOutputContext output) {
//do the stuff that was in the if-else block in the "readProfile" part
}
}
//... at the branching part:
MyInputContext input; //build this here
MyOutputContext output; //build this here
switch (purpose) {
case "readProfile":
// no need to always instantiate this, it should be stateless...
new ReadProfileStrategy().execute();
break;
//... left out for sake of brevity
}
Рефакторинг, шаг 2
Если это сделано, вы можете добавить строковые идентификаторы к интерфейсу и самим экземплярам, и вообще избавиться от оператора if-else или switch, вы можете создать карту, заполненную даже через контейнер IOC (например), чтобы современный и полностью гибкий.
class ReadProfileStrategy implements MyStrategy {
String getID() {
return "readProfile";
}
void execute(MyInputContext input, MyOutputContext output) {
//do the stuff that was in the if-else block in the "readProfile" part
}
}
В классе, когда обрабатываются запросы
private final Map<String, MyStrategy> strategyMap; //fill the map using your favorite approach, like using Spring application context, using the getCode() to provide the key of the map
В логике обработки:
MyStrategy strategy = strategyMap.get(purpose);
if(strategy!=null) {
strategy.execute();
}
else {
//handle error here
}
Перечисления могут помочь - вы также можете добавить к ним функциональность.
public void test(String purpose) {
if (purpose.equals("readProfile")) {
// Read.
} else if (purpose.equals("writeProfile")) {
// Write.
}
}
enum Purpose {
readProfile {
@Override
void doIt() {
// Read.
}
},
writeProfile {
@Override
void doIt() {
// Write.
}
};
abstract void doIt();
}
public void test2(String purpose) {
Purpose.valueOf(purpose).doIt();
}
Это может быть за рамками, но просто наблюдение
попробуйте использовать
if("readProfile".equals(purpose){}
вместо
if(purpose.equals("readProfile"){}
,
Это поможет избежать исключения нулевого пинтера
Вы можете попробовать использовать какой-нибудь Action-Interface с реализациями для каждого блока и предварительно загрузить карту с конкретными реализациями этого действия.
interface Action {
void execute();
}
Map<String, Action> actions = new HashMap<>();
actions.put("readProfile", new Action() { ... });
actions.put("writeProfile", new Action() { ... });
actionMap.get(purpose).execute();
Это также снизит вашу цикломатическую сложность. Конечно, вы должны предварительно загрузить карту только один раз.
Что ж, если имеет смысл отделить код внутри условия if-else от другого класса, возможно, используйте шаблон Factory. Также заставьте все разделенные классы реализовать общий интерфейс (например: MyActivity.class
) с помощью метода, такого как execute()
,
Фабрика решает, какой объект (ReadProfile.class
, WriteProfile.class
и т.д.) должен быть создан на основе строки, которую вы передаете, а затем вызвать execute()
метод.
MyActivity obj = MyFactory.createMyActivity(String)
obj.execute(...);