Как сделать этот оператор if более элегантным в Java?

#java #if-statement #optimization

#java #if-оператор #оптимизация

Вопрос:

В моем проекте есть логика, похожая на следующий код, и будет много типов, но это слишком запутанно для написания, я хочу спросить, есть ли какой-либо более элегантный способ.

 int a = 3,b = 5;
int type = 2;
if (type == 1) {
    if (a > 1) {
        System.out.println("a > 1");
    } else if (b > 3) {
        System.out.println("b > 3");
    }
}
if (type == 2) {
    if (b > 3) {
        System.out.println("b > 3");
    } else if (a > 1) {
        System.out.println("a > 1");
    }
}
 

Комментарии:

1. Оператор switch сделал бы его немного лучше.

2. Может быть, оператор переключения? В этом коде трудно выделить шаблон, который особенно поддается рефакторингу, чтобы сделать его «элегантным». Имейте в виду, что «элегантность» и «оптимизация» часто являются противоположными целями.

3. существуют ли только 2 переменные a и b ?

4. @Bohemian ♦ В реальном проекте в операторе if есть три условия, a и b приведены только для иллюстрации

5. Не видя фактического кода (а не чего-то похожего на фактический код), трудно дать совет по рефакторингу. Это было бы похоже на то, как врач осматривает мою сестру, чтобы убедиться, что я здоров.

Ответ №1:

Используйте Map<Integer, BiConsumer<Integer, Integer> .

 Map<Integer, BiConsumer<Integer, Integer> map = new HashMap<>();
map.put(1, (a, b) -> {
  if (a > 1) {
      System.out.println("a > 1");
  } else if (b > 3) {
      System.out.println("b > 3");
  }
});
map.put(2, (a, b) -> {
  if (b > 3) {
      System.out.println("b > 3");
  } else if (a > 1) {
      System.out.println("a > 1");
  }
});
 

Затем использовать:

 Optional.ofNullable(map.get(type)).ifPresent(c -> c.accept(a, b));
 

Комментарии:

1. И это лучше, чем код OP, потому что …?

2. @Holger потому что карта может быть определена, заполнена и протестирована в другом месте с точки использования. Популяция может быть даже через весенние бобы, причем каждая пара условие / лямбда является своим собственным бобом. Речь идет о развязке. Используя эту абстракцию, вы можете изменять способ, когда и где заполняется карта, создавать собственные карты для каждой среды, поддерживать и обновлять код, а также масштабировать его. Если вы запекаете его в виде жесткого кода, вы не сможете реально выполнить ни одну из этих вещей.

3. Поскольку эта мотивация не была указана в вопросе, не должна ли она быть в вашем ответе, а не в комментарии?

Ответ №2:

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

Если это так, вы могли бы объявить

 interface Checker {
    String check();
}
 

а затем выполните:

 Checker ca = () -> a > 1 ? "a > 1" : null;
Checker cb = () -> b > 3 ? "b > 3" : null;

Checker[][] checkers = {
        null,
        {ca, cb},
        {cb, ca},
};

for (var checker : checkers[type]) {
    var message = checker.check();
    if (message != null) {
        return message;
    }
}
return null;
 

Это определяет каждое условие только один раз, независимо от того, сколько существует типов.

(вы также можете использовать выражение switch, а не массив)

Ответ №3:

Вы могли бы попробовать использовать оператор switch, это сделало бы его более элегантным. Что-то вроде:

         int a = 3, b =  5;
        int type = 2;

        switch (type)
        {
            case 1:
                if (a>1) System.out.println("a > 1");
                else if (b > 3) System.out.println("b > 3");
                break;
            

            case 2:
                if(b > 3) System.out.println("b > 3");
                else if(a > 1) System.out.println("a > 1");
                break;
        }
 

Я надеюсь, что это было несколько полезно, удачи!

Комментарии:

1. Упс, я думаю, вы правы. В последние несколько дней я экспериментировал с операторами C switch, поэтому я немного перепутал. Редактирование, спасибо за комментарий.

Ответ №4:

Если проверка и действие повторяются, вы можете избежать некоторого повторения с помощью чего-то подобного. Самое плохое в этом — это работа с побочными эффектами…

 private boolean checkA(int a) {

    if (a > 1) {
       System.out.println("a > 1");
       return true;
    } 
    return false;
}

private boolean checkB(int b) {

    if (b > 3) {
       System.out.println("b > 3");
       return true;
    } 
    return false;
}


...

switch (type)
{
     case 1: if (!checkA(a)) {
                  checkB(b);
             }
             break;
     case 2: if (!checkB(b)) {
                  checkA(a);
             }
             break;

}
 

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

еще один неприятный способ, которым этим можно «злоупотреблять», — это

     boolean dummy;
    switch (type)
    {
         case 1: dummy = checkA(a) || checkB(b);
                 break;
         case 2: dummy = checkB(b) || checkA(a);
                 break;

    }
 

Ответ №5:

Начните с использования классов для ваших разных типов. Это упростит добавление дополнительных типов с различным поведением. Затем вы можете рассмотреть вопрос об упрощении классов по отдельности. Вы также сможете протестировать их отдельно с помощью модульных тестов.

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

 new Type2(3, 5).process();
 

Если у вас есть следующие классы

 public interface Processor {
    void process();
}

public class Type1 implements Processor {

    private int a;
    private int b;
    
    public Type1(int a, int b) {
        this.a = a;
        this.b = b;
    }
    
    @Override
    public void process() {
        if (a > 1) {
            System.out.println("a > 1");
        } else if (b > 3) {
            System.out.println("b > 3");
        }
    }

}

 public class Type2 implements Processor {

    private int a;
    private int b;
    
    public Type2(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public void process() {
        if (b > 3) {
            System.out.println("b > 3");
        } else if (a > 1) {
            System.out.println("a > 1");
        }
    }

}