Улучшение разборчивости условных выражений

Я строю 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(...);
Другие вопросы по тегам