Альтернативное написание нулевой проверки между двумя переменными? - PullRequest
3 голосов
/ 26 сентября 2019

У меня немного загадка.У меня есть 2 объекта A и B.Они могут прийти как null или не null, и мне приходится делать какую-то другую бизнес-логику во всех этих ситуациях.

На данный момент у меня есть:

if(A != null && B != null) { /* operation1 */ } 
if(A == null && B == null) { /* operation2 */ }
if(A != null && B == null) { /* operation3 */ } 
if(A == null && B != null) { /* operation4 */ }

МожетВы порекомендуете мне способ написать эти if с более элегантным способом?

Ответы [ 5 ]

4 голосов
/ 26 сентября 2019

Чтобы не проверять одно и то же условие несколько раз:

if (A != null) {
    if (B != null) {
        // operation1
    } else {
        // operation3
    }
} else {
    if (B != null) {
        // operation4
    } else {
        // operation2
    }
}

Но это не делает код более элегантным.

1 голос
/ 26 сентября 2019

На самом деле, если вы хотите, чтобы ваши условия if были видны в вызывающем методе, ваш код является лучшим вариантом, чем решение, предложенное для реализации условий if.Все эти операторы if-else и внутренние операторы if-else делают менее очевидными условия, запускающие каждую операцию.Таким образом, вы можете сохранить свой подход.См. это сообщение в блоге от Джеффа Этвуда

Одно небольшое улучшение, которое вы можете сделать, - это извлечь методы из условий if и назвать новые методы так, чтобы документировать ваши решения:

if( isYouHaveACarAndEnoughGas(A, b)) { //operation to drive}
if( isYouHaveACarAndNoGas(A, b)) { //operation to walk}
...
private boolean isYouHaveACarAndEnoughGas(...){ return A != null && B != null; }

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

interface Operation{
    void execute();
}

class OperationBuilder {
    public static Operation build(TypeA A, TypeB B){
        if(A != null && B != null) { return new OperationOne(); }
        if(A == null && B == null) { return new OperationTwo(); }
        if(A != null && B == null) { /* operation3 */ }
        if(A == null && B != null) { /* operation4 */ }
    }

    class OperationOne implements Operation{
        private OperationOne(){...}
        @Override
        void execute(){...}
    }
    class OperationTwo implements Operation{
        private OperationTwo(){}
        @Override
        void execute(){...}
    }
}

Чтобы использовать это, вы просто

Operation op = OperationBuilder.build(A, B) ;
op.execute();

А вот почему это может быть лучше, чем выначало .Представьте, что у вас есть

if(codition1){
  //complicated stuff here that makes you scroll and scroll
}
if(codition2){
  //complicated stuff here that makes you scroll and scroll
}
etc

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

1 голос
/ 26 сентября 2019

Вы можете сделать следующее, хотя это спорно, является ли это лучше или хуже, чем то, что у вас уже есть (что не плохо):

1002 *
0 голосов
/ 26 сентября 2019

Вы можете сделать что-то подобное в сочетании с ответом Джеспера :

Predicate<Object> isNull = Objects::isNull; // or o -> o != null
Predicate<Object> isNotNull = Objects::nonNull; // or o -> o == null

if (isNull.test(A)) { 
    if (isNotNull.test(B)) {
        /* operation1 */ 
    }
    else {
        /* operation2 */
    }
}
else {
    if(isNull.test(B)) { 
        /* operation3 */ 
    }
    else {
        /* operation4 */ 
    }
}
0 голосов
/ 26 сентября 2019

Вот эквивалентное решение с использованием вложенного if / else:

if(A != null)
{
    if(B != null)
    {
        /* operation1 */
    }
    else
    {
        /* operation3 */
    }
}
else
{
    if(B != null)
    {
        /* operation4 */
    }
    else
    {
        /* operation2 */
    }
}

Как вы можете ясно видеть, семантика "do xyz, если выполняются условия a ... b ... c ..."намного менее ясно с этим подходом.Следовательно, предлагаемый вами «не элегантный» подход на самом деле является логичным и понятным фрагментом кода.В лучшем случае вы можете сделать что-то вроде следующего:

// Using a helper class with method names that 
// clearly communicate intent/meaning in the null checks:
class Logics
{
    public static boolean allNull(Object... objs)
    {
        for(Object o : objs)
            if(o != null)
                return false;
        return true;
    }
    public static boolean noneNull(Object... objs)
    {
        for(Object o : objs)
            if(o == null)
                return false;
        return true;
    }
}
...
if(Logics.noneNull(A, B)) { /* operation1 */ } 
if(Logics.allNull(A, B)) { /* operation2 */ }
if(A != null && B == null) { /* operation3 */ } 
if(A == null && B != null) { /* operation4 */ }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...