Java 8: без использования if else [duplicate]

Использование тегов скриптов:

  • добавить блок <script>...</script>, содержащий ваш многострочный текст в тег head;
  • получить ваш многострочный текст как есть ... ( следить за кодировкой текста: UTF-8, ASCII)
    <script>
    
        // pure javascript
        var text = document.getElementById("mySoapMessage").innerHTML ;
    
        // using JQuery's document ready for safety
        $(document).ready(function() {
    
            var text = $("#mySoapMessage").html(); 
    
        });
    
    </script>
    
    <script id="mySoapMessage" type="text/plain">
    
        <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="...">
           <soapenv:Header/>
           <soapenv:Body>
              <typ:getConvocadosElement>
                 ...
              </typ:getConvocadosElement>
           </soapenv:Body>
        </soapenv:Envelope>
    
        <!-- this comment will be present on your string -->
        //uh-oh, javascript comments...  SOAP request will fail 
    
    
    </script>
    
23
задан Peter Mortensen 17 April 2018 в 10:55
поделиться

5 ответов

Проблема

(1) Кажется, вы смешиваете разные аспекты - управляющий поток и логика домена .

element.ifExist(() -> { ... }).otherElementMethod();
          ^                      ^
        control flow method     business logic method

(2) Неясно, как должны вести себя методы после метода потока управления (например, ifExist, ifNotExist). Если они всегда выполняются или вызывается только при условии (аналогично ifExist)?

(3) Имя ifExist подразумевает операцию терминала, поэтому возвращаться нечего - void , Хорошим примером является void ifPresent(Consumer) из Optional .

Решение

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

Интерфейс прост и состоит из двух методов бесконтактного управления потоком - ifTrue и ifFalse.

несколько способов создания объекта Condition. Я написал статический заводский метод для вашего экземпляра (например, element) и условие (например, Element::exist).

public class Condition<E> {

    private final Predicate<E> condition;
    private final E operand;

    private Boolean result;

    private Condition(E operand, Predicate<E> condition) {
        this.condition = condition;
        this.operand = operand;
    }

    public static <E> Condition<E> of(E element, Predicate<E> condition) {
        return new Condition<>(element, condition);
    }

    public Condition<E> ifTrue(Consumer<E> consumer) {
        if (result == null)
            result = condition.test(operand);

        if (result)
            consumer.accept(operand);

        return this;
    }

    public Condition<E> ifFalse(Consumer<E> consumer) {
        if (result == null)
            result = condition.test(operand);

        if (!result)
            consumer.accept(operand);

        return this;
    }

    public E getOperand() {
        return operand;
    }

}

Кроме того, мы можем интегрировать Condition в Element:

class Element {

    ...

    public Condition<Element> formCondition(Predicate<Element> condition) {
        return Condition.of(this, condition);
    }

}

Я поощряю шаблон:

  • работает с Element;
  • получает Condition;
  • ] управляйте потоком с помощью Condition;
  • переключитесь на Element,
  • продолжайте работу с Element.

] Результат

Получение Condition с помощью Condition.of:

Element element = new Element();

Condition.of(element, Element::exist)
             .ifTrue(e -> { ... })
             .ifFalse(e -> { ... })
         .getOperand()
             .otherElementMethod();

Получение Condition на Element#formCondition:

Element element = new Element();

element.formCondition(Element::exist)
           .ifTrue(e -> { ... })
           .ifFalse(e -> { ... })
       .getOperand()
           .otherElementMethod();

Обновление 1:

Для других методов тестирования идея остается той же.

Element element = new Element();

element.formCondition(Element::isVisible);
element.formCondition(Element::isEmpty);
element.formCondition(e -> e.hasAttribute(ATTRIBUTE));

Обновление 2:

Это хорошая причина переосмыслить код дизайн. Ни один из 2 фрагментов не замечателен.

Представьте, что вам нужно actionC в e0.exist(). Как изменить ссылку на метод Element::actionA?

Он будет возвращен в лямбда:

e0.ifExist(e -> { e.actionA(); e.actionC(); });

, если вы не обернете actionA и actionC одним методом (что звучит ужасно):

e0.ifExist(Element::actionAAndC);

Теперь лямбда еще менее читаема, тогда if был.

e0.ifExist(e -> {
    e0.actionA();
    e0.actionC();
});

Но сколько усилий мы сделаем для этого? И сколько усилий мы приложим для поддержания всего этого?

if(e0.exist()) {
    e0.actionA();
    e0.actionC();
}
3
ответ дан Andrew Tobilko 17 August 2018 в 11:54
поделиться
  • 1
    Почему getOperand().otherElementMethod() лучше, чем второе утверждение element.otherElementMethod() после условного материала? – Michael 16 April 2018 в 22:09
  • 2
    Также не используйте Function<E, Boolean>, используйте Predicate – Michael 16 April 2018 в 22:10
  • 3
    @Michael, Это было бы почти то же самое, но OP хочет создать цепочки. О Predicate - вы правы, спасибо – Andrew Tobilko 17 April 2018 в 08:33

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

Java 8 имеет ограниченную выразительность:

Optional<Elem> element = ...
element.ifPresent(el -> System.out.println("Present " + el);
System.out.println(element.orElse(DEFAULT_ELEM));

Здесь map может ограничивать представление элемента:

element.map(el -> el.mySpecialView()).ifPresent(System.out::println);

Java 9: ​​

element.ifPresentOrElse(el -> System.out.println("Present " + el,
                        () -> System.out.println("Not present"));

В общем случае две ветви асимметричны.

28
ответ дан Joop Eggen 17 August 2018 в 11:54
поделиться
  • 1
    @Andrew Я не ожидал 4 upvotes, так как я просто хотел указать, что пользовательский exists, вероятно, указывает на то, что Optional будет более подходящим. И forEach определенно ошибается, спасибо. – Joop Eggen 16 April 2018 в 13:39
  • 2
    Также есть Optional.or в Java 9+ – Federico Peralta Schaffner 16 April 2018 в 19:45

Он называется свободным интерфейсом '. Просто измените тип возвращаемого значения и return this;, чтобы вы могли привязать методы:

public MyClass ifExist(Consumer<Element> consumer) {
    if (exist()) {
        consumer.accept(this);
    }
    return this;
}

public MyClass ifNotExist(Consumer<Element> consumer) {
    if (!exist()) {
        consumer.accept(this);
    }
    return this;
}

Вы могли бы получить немного более интересный и вернуть промежуточный тип:

interface Else<T>
{
    public void otherwise(Consumer<T> consumer); // 'else' is a keyword
}

class DefaultElse<T> implements Else<T>
{
    private final T item;

    DefaultElse(final T item) { this.item = item; }

    public void otherwise(Consumer<T> consumer)
    {
        consumer.accept(item);
    }
}

class NoopElse<T> implements Else<T>
{
    public void otherwise(Consumer<T> consumer) { }
}

public Else<MyClass> ifExist(Consumer<Element> consumer) {
    if (exist()) {
        consumer.accept(this);
        return new NoopElse<>();
    }
    return new DefaultElse<>(this);
}

Образец использование:

element.ifExist(el -> {
    //do something
})
.otherwise(el -> {
    //do something else
});
19
ответ дан Michael 17 August 2018 в 11:54
поделиться
  • 1
    OP захотелось избежать проверки состояния дважды - if the exist condition is true, no need to check ifNotExist – Eran 16 April 2018 в 12:28
  • 2
    @Eran Если это логический флаг, я не вижу причин беспокоиться о том, чтобы что-то переделать. Тем не менее, я обновил свой ответ. – Michael 16 April 2018 в 12:31
  • 3
    И всего за 45 строк вам удалось сохранить -2 строки. Типичная Java: D – Eric Duminil 16 April 2018 в 14:38
  • 4
    @ EricDuminil Ну, интерфейс и 2 класса являются универсальными и многоразовыми. – Michael 16 April 2018 в 14:52
  • 5
    да, это именно то, что я хочу использовать свободный интерфейс, но с вашим решением, если я не проверю иначе, я не могу связать другой метод элемента, например: element.ifExist (...). otherElementMethod () – yelliver 16 April 2018 в 18:14

Вы можете использовать один метод, который принимает двух пользователей:

public void ifExistOrElse(Consumer<Element> ifExist, Consumer<Element> orElse) {
    if (exist()) {
        ifExist.accept(this);
    } else {
        orElse.accept(this);
    }
}

Затем вызовите его с помощью:

element.ifExistOrElse(
  el -> {
    // Do something
  },
  el -> {
    // Do something else
  });
11
ответ дан Peter Mortensen 17 August 2018 в 11:54
поделиться
  • 1
    Мне понравился этот. Это может быть упрощено до (exist() ? ifExist : orElse).accept(this); – Andrew Tobilko 16 April 2018 в 12:45
  • 2
    Спасибо за ваш ответ, это хороший подход, но на самом деле я хочу использовать свободный стиль, это не мое ожидание – yelliver 16 April 2018 в 18:11
  • 3
    @yelliver Зачем вам нужен «свободный стиль»? Какую проблему он решает? Этот ответ имеет силу намного проще и проще и проще в использовании. У вас должна быть very серьезная причина усложнить ситуацию. (Обратите внимание, что это означает, что вам нужна очень хорошая причина не просто использовать синтаксис if / else в первую очередь.) – jpmc26 16 April 2018 в 21:50
  • 4
    @yelliver Я хотел бы отметить, что я думаю, что этот ответ лучше подходит для примера, который вы редактировали, чем для свободного интерфейса. – jpmc26 17 April 2018 в 03:40

Как некоторые предложения, у меня есть идея кэшировать логическое значение:

public class Element {
    private Boolean exist;

    public void ifExist(Consumer<Element> consumer) {
        if (exist == null) {
            exist = exist();
        }
        if (exist) {
            consumer.accept(this);
        }
    }

    public void ifNotExist(Consumer<Element> consumer) {
        if (exist == null) {
            exist = exist();
        }
        if (!exist) {
            consumer.accept(this);
        }
    }
}
-1
ответ дан yelliver 17 August 2018 в 11:54
поделиться
  • 1
    моя идея очень близка к этому - ленивая загрузка и кэширование результата состояния. Пожалуйста, посмотрите мои 2 обновления. – Andrew Tobilko 17 April 2018 в 09:24
  • 2
    Имейте в виду, что этот подход может не работать слишком хорошо, если вы вводите многопоточность в свое приложение. – Sean Burton 17 April 2018 в 12:02
  • 3
    @SeanBurton, мы говорим о многопоточности здесь? – Andrew Tobilko 17 April 2018 в 12:13
  • 4
    Не так ли? Нигде OP особо не упоминает, что их код является однопоточным, поэтому небезопасно предполагать, что это так. – Sean Burton 18 April 2018 в 10:14
  • 5
    @SeanBurton да, могу ли я преобразовать его в AtomicBoolean для потоковой безопасности? – yelliver 18 April 2018 в 11:54
Другие вопросы по тегам:

Похожие вопросы: