Чистый отчет IF
В Java, следующий оператор выглядит очень беспорядочно, когда у вас много терминов:
if(a.equals("x") || a.equals("y") || a.equals("z") || Any number of terms...... )
//Do something
Есть ли более чистый способ выполнения одного и того же действия, я хотел бы, чтобы мой код был максимально читабельным.
ПРИМЕЧАНИЕ. x, y и z являются просто заполнителями для любой строки любой длины. Здесь может быть 20 строковых терминов переменной длины, если каждое условие OR'd вместе
Спасибо
Ответы
Ответ 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
}
}