Подтвердить что ты не робот

Чистый отчет IF

В Java, следующий оператор выглядит очень беспорядочно, когда у вас много терминов:

if(a.equals("x") || a.equals("y") || a.equals("z") || Any number of terms...... )
    //Do something

Есть ли более чистый способ выполнения одного и того же действия, я хотел бы, чтобы мой код был максимально читабельным.

ПРИМЕЧАНИЕ. x, y и z являются просто заполнителями для любой строки любой длины. Здесь может быть 20 строковых терминов переменной длины, если каждое условие OR'd вместе

Спасибо

4b9b3361

Ответ 1

Set<String> stuff = new HashSet<String>();
stuff.add("x");
stuff.add("y");
stuff.add("z");
if(stuff.contains(a)) {
    //stuff
}

Если это замкнутый цикл, вы можете использовать статический набор.

static Set<String> stuff;
static {
    stuff = new HashSet<String>();
    stuff.add("x");
    stuff.add("y");
    stuff.add("z");
}

//Somewhere else in the cosmos

if(stuff.contains(a)) {
    //stuff
}

И если вы хотите быть уверены, что ничего не меняется, пока вы не смотрите.

Set<String> test = Collections.unmodifiableSet(new HashSet<String>() {
        {
            add("x");
            add("y");
            add("z");
        }
    });

Если вы просто хотите получить некоторую логику там для нескольких жестко закодированных условий, то один из операторов switch или if с новыми решениями может быть лучше. Но если у вас много условий, тогда было бы неплохо отделить вашу конфигурацию от логики.

Ответ 2

Как вы думаете, выглядит "нечистым" об этом?

Если у вас есть сложная логическая логика, вы можете разделить ее на отдельные логические переменные и ссылаться на них в инструкции if.

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

Ответ 3

В качестве альтернативы, если вы используете Java 7+, вы можете использовать строки в switch/case. Например (я извлек это из документа Oracle и изменил его)

         switch (str) {

             case "x":
             case "y":
             case "z":
                 //do action
                 break;
             default:
              throw new IllegalArgumentException("argument not matched "+str);

    }

Вот ссылка

Ответ 4

Используйте регулярное выражение

If (a.matches("[xyz]")){
    // matches either "x", "y", or "z"

или, для более длинных строк,

If (a.matches("one|two|three")){
    // matches either "one", "two" or "three"

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

Но в конце концов, самый приятный способ - это, вероятно, оставить вещи такими, какие они есть, с настройкой на форматирование:

if (a.equals("x") || 
    a.equals("y") || 
    a.equals("z")
    ){

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

Ответ 5

Достижение семантики

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

Каждая свободная степень свободы означает воздействие не только на еще одну ошибку, но и на еще один класс ошибок.

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

  • ясная и явная семантика;
  • жесткое ограничение степеней свободы, которой нужно ухаживать;
  • O (1) сложность времени по сравнению с O (n) сложностью вашего кода.

Это код, необходимый для реализации идиомы на основе набора:

static final Set<String> matches = 
                            unmodifiableSet(new HashSet<>(asList("a","b","c")));
...

if (matches.contains(a)) // do something;

* Я подразумеваю import static java.util.Arrays.asList и import static java.util.Collections.unmodifiableSet

Ответ 6

Чтение в основном форматирование

Не читается...

if(a.equals("x") || a.equals("y") || a.equals("z") || Any number of terms...... )
    //Do something

Теперь легко в реальном...

if(a.equals("x") || 
   a.equals("y") || 
   a.equals("z") ||
   Any number of terms...... )
    //Do something

Чтение очень субъективно для человека, читающего исходный код.

Если я столкнулся с кодом, который реализует коллекции, циклы или один из многих других сложных ответов здесь. Я покачал головой в недоумении.

Отделить логику от проблемы

Вы смешиваете две разные вещи. Существует проблема упрощения чтения бизнес-логики и проблемы реализации бизнес-логики.

 if(validState(a))
     // Do something

Как вы реализуете validState, не имеет значения. Важно то, что код с оператором if читается как бизнес-логика. Это не должна быть длинная цепочка булевых операций, которые скрывают намерение того, что происходит.

Вот пример читаемой бизнес-логики.

if(!isCreditCard(a)) {
    return false;
}
if(isExpired(a)) {
    return false;
}
return paymentAuthorized(a);

На некотором уровне должен быть код, который обрабатывает основную логику, строки, массивы и т.д. и т.д., но не должен быть на этом уровне.

Если вы обнаружите, что вам часто приходится проверять, равна ли строка цепочке других строк. Поместите этот код в класс командной строки. Отделите его от своей работы и сохраните свой код. Обеспечивая это, показывает, что вы действительно пытаетесь сделать.

Ответ 7

Вы можете использовать Arrays.asList(). Это самый простой подход и меньше подробностей.

Arrays.asList("x","y","z"...).contains(a)

По соображениям производительности, если ваша коллекция слишком велика, вы можете поместить данные в поиск HashSet в течение постоянного времени.

Пример сделайте свой собственный метод утилиты

public final class Utils{

    private Utils(){}//don't let instantiate

    public static <T> boolean contains(T a,T ... x){
      return new HashSet<>(Arrays.asList(x)).contains(a);
    }
}    

Затем в вашем клиентском коде:

if(Utils.contains(a,"x","y","z","n")){
  //execute some code
}

Ответ 8

С небольшой помощью вы можете получить синтаксический сахар более приятного оператора if с небольшим количеством накладных расходов. Чтобы уточнить рекомендации Тима и рекомендацию Йеско, еще немного...

public abstract class Criteria {

  public boolean matchesAny( Object... objects ) {
    for( int i = 0, count = objects.length; i < count; i++ ) {
      Object object = objects[i];
      if( matches( object ) ) {
        return true;
      }
    }
    return false;
  }

  public boolean matchesAll( Object... objects ) {
    for( int i = 0, count = objects.length; i < count; i++ ) {
      Object object = objects[i];
      if( !matches( object ) ) {
        return false;
      }
    }
    return true;
  }

  public abstract boolean matches( Object object );

}

public class Identity extends Criteria {

  public static Identity of( Object self ) {
    return new Identity( self );
  }

  private final Object self;

  public Identity( Object self ) {
    this.self = self;
  }

  @Override
  public boolean matches( Object object ) {
    return self != null ? self.equals( object ) : object == null;
  }

}

Ваш if-statement будет выглядеть следующим образом:

if( Identity.of( a ).matchesAny( "x", "y", "z" ) ) {
  ...
}

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

Даже с улучшенным синтаксисом это условное выражение все еще немного сложнее. Дальнейший рефакторинг может привести к экстернализации терминов "x", "y", "z" и перемещению выражения в метод, чье имя четко определяет его намерение:

private static final String [] IMPORTANT_TERMS = {
  "x",
  "y",
  "z"
};

public boolean isImportant( String term ) {
  return Identity.of( term ).matchesAny( IMPORTANT_TERMS );
}

... и ваш оригинальный if-statement, наконец, будет сведен к...

if( isImportant( a ) ) {
  ...
}

Это намного лучше, и теперь метод, содержащий ваше условное выражение, может с большей готовностью сосредоточиться на Doing One Thing.

Ответ 9

Независимо от того, чего вы пытаетесь достичь, это

if(a.equals("x") || a.equals("y") || a.equals("z") || Any number of terms...... )
    //Do something

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

простейшее решение для меня было бы выразить ваше намерение вместо явного.

Попробуйте сделать это вместо:

   public class SomeClass{
    public void SomeMethod(){
        if ( matchesSignificantChar(a) ){
          //doSomething
        }    
    }
    private bool matchesSignificantChar(String s){
        return (s.equals("x") || s.equals("y") || s.equals("z") || Any number of terms......    )
      }
   }

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

Однако это все еще не очень расширяемо. Если вы попытаетесь сделать его более чистым, вы можете извлечь логический метод в другой класс и передать его в качестве делегата в SomeClass'es Constructor или даже SomeMethod. Также вы можете заглянуть в шаблон стратегии для еще большей экстенсивности.

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

Ответ 10

Я использую следующий шаблон

boolean cond = false; // Name this variable reasonably

cond = cond || a.equals("x");
cond = cond || a.equals("y");
cond = cond || a.equals("z");

// Any number of terms......

if (cond) {
    // ...
}

Примечание: в куче не создаются объекты. Также вы можете использовать любые условия, а не только "равно".

В рубине для этой цели можно использовать оператор ||= как cond ||= a.equals("x").

Ответ 11

Ответ Set хорош. Когда вы не сравниваете членство в коллекции, вы можете также выделить некоторые или все условные утверждения в методы. Например

if (inBounds(x) && shouldProcess(x) ) {
}

Ответ 12

Если a гарантированно имеет длину 1, вы можете сделать:

if ("xyz".indexOf(a) != -1)

Ответ 13

Один очень хороший способ сделать что-то вроде этого - использовать значения ASCII, предполагая, что ваш фактический случай здесь, где a - это char или одна символьная строка. Преобразуйте a в его целочисленный эквивалент ASCII, затем используйте что-то вроде этого:

Если вы хотите проверить, что a является "t", "u", "v",..., "z", тогда сделайте.....

Если (val >= 116 & val <= 122) {//здесь код}

Ответ 14

Я предпочитаю использовать regexp, как несколько ребята, написал верхний. Но также вы можете использовать следующий код

private boolean isOneMoreEquals(Object arg, Object... conditions) {
    if (conditions == null || arg == null) {
        return false;
    }
    for (int i = 0, d = conditions.length; i < d; i++) {
        if (arg.equals(conditions[i])) {
            return true;
        }
    }
    return false;
}

поэтому ваш код будет следующим:

if (isOneMoreEquals(a, "x", "y", "z") {
    //do something
}

Ответ 15

Предполагая, что ваши "x", "y" и "z" могут иметь произвольную длину, вы можете использовать

if (0 <= java.util.Arrays.binarySearch(new String[] { "x", "y", "z" }, a)) {
    // Do something
}

Просто убедитесь, что вы указываете свои позиции в лексикографическом порядке, как требуется binarySearch(). Это должно быть полностью совместимо с Java 1.2, и оно должно быть более эффективным, чем решения, использующие коллекции Java.

Конечно, если ваши "x", "y" и "z" - все одиночные символы, а a также является символом, вы можете использовать if (0 <= "xyz".indexOf(a)) { ... } или

switch (a) {
  case 'x': case 'y': case 'z':
    // Do something
}

Ответ 16

Если x, y, z... является последовательным, вы можете использовать if (a >= 'x' && a <= '...'), если нет, вы можете использовать ArrayList или просто Массивы.

Ответ 17

Я думаю, что самый чистый и быстрый способ - поместить значения в массив.

String[] values={"value1","value2","value3"};
for (string value : values) {
    if (a.equals(value){
        //Some code
    }
}