2016-04-11 2 views
1

Возможна ли условная композиция Потребителей в Java 8? В основном я ищу создать пользовательский интерфейс Lambda, похожий на Consumer, но который работает только с одним типом объекта. Давайте назовем это, Stateful и содержит несколько статусов (мы скажем два для этого примера):Java 8 Пользовательский потребитель

public class Stateful { 
    private int status1; 
    private int status2; 
} 

У нас есть много областей, в нашем коде, где мы делаем операцию на Stateful и, если статус изменился, мы сделали бы еще одну операцию. Мне было интересно, можем ли мы использовать композицию, чтобы справиться с этим более компактным и элегантным образом. Сейчас мы будем делать что-то вроде:

SimpleEntry<Integer, Integer> oldStates = new SimpleEntry(stateful.getStatus1(), stateful.getStatus2()); 

applyLogicOnStateful(stateful); //do some operation that may change state values 
if(isStatusChanged(oldStates, stateful) { //compare oldStates integers to status integers 
    doSomethingElse(stateful); 
} 

, где я думаю, что-то, как это будет выглядеть лучше:

statefulConsumer 
.accept((stateful)->applyLogicOnStateful(stateful)) 
.ifStatusChanged((stateful)->doSomethingElse(stateful)); 

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

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

+1

«Компактный и элегантный» способ сделать это в вашем первом например, используя совершенно нормальный, читаемый оператор 'if'. –

+0

Я почтительно не согласен: нужно сохранить состояние переменной, сделать некоторую логику, а затем сделать сравнение громоздкой и определенно не СУХОЙ. Я полагаю, что у меня может быть модифицированный потребитель, который возвращает логическое значение, если статус изменяется. – IcedDante

+2

Я думаю, что единственный способ сделать это - если ваш applyLogicOnStateful возвращает логическое значение, если ваш статус изменился, или если вы создаете предикат с ожидаемыми значениями, прежде чем вызывать все ваши операции (что то же самое, что вы делаете сейчас, просто более элегантно) –

ответ

0

Вот функция, которая вернет Consumer<Stateful>, которая будет извлекать прежнее состояние, выполнять изменения, сравнивать результаты и условно работать с измененным объектом.

public static Consumer<Stateful> getStatefulConsumer(
     Function<Stateful,SimpleEntry<Integer,Integer>> getStatus,     // extract status from Stateful 
     Consumer<Stateful> applyLogic,            // apply business logic 
     BiPredicate<SimpleEntry<Integer,Integer>,SimpleEntry<Integer,Integer>> compareState, // test statuses for change 
     Consumer<Stateful> onChange)            // doSomethingElse 
{ 
    return stateful -> { 
     SimpleEntry<Integer,Integer> oldStatus = getStatus.apply(stateful); 
     applyLogic.accept(stateful); 
     if(!compareState.test(oldStatus, getStatus.apply(stateful))){ 
      onChange.accept(stateful); 
     } 
    }; 
} 

Вы могли бы использовать его как это:

Consumer<Stateful> ifChanged = getStatefulConsumer(s -> new SimpleEntry<> (s.status1, s.status2), 
    s -> changeSomething(s), Objects::equals, s->doSomething(s)); 

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

+0

Спасибо Хэнк. Я действительно ошибся в описании метода 'statusChanged', поскольку для его определения требуется старая информация о состоянии. Я отредактирую свой оригинальный вопрос, чтобы отразить это, и мои извинения за путаницу – IcedDante

+0

ОК, я отредактировал свой ответ, чтобы учесть ваши отзывы. Дайте мне знать, что вы думаете. –

+1

Одним из преимуществ возврата его в качестве «Потребителя » является то, что вы можете использовать его в различных API, таких как 'Stream.forEach()', 'Collection.forEach()', 'Optional.ifPresent',' CompletableFuture.thenAccept () 'и т. д. –

0

Решение Я работаю с прямо сейчас, чтобы создать интерфейс Lambda, который принимает Stateful экземпляр и два потребителя в качестве входных данных:

public interface StatefulConsumer { 
    void accept(Stateful stateful, Consumer<Stateful> consumer, Consumer<Stateful> ifStateChangedConsumer); 
} 

и реализация:

final StatefulConsumer IfStateChanges = new StatefulConsumer() { 
    @Override 
    public void accept(Stateful stateful, Consumer<Stateful> consumer, Consumer<Stateful> ifStateChangedConsumer) { 
     SimpleEntry<Integer, Integer> oldStates = new SimpleEntry(stateful.getStatus1(), stateful.getStatus2()); 

     consumer.accept(stateful); //do some operation that may change state values 
     if(isStatusChanged(oldStates, stateful) { //compare oldStates integers to status integers 
      ifStateChangedConsumer.accept(stateful); 
     } 
    } 
}; 

, которые могут быть называемый так: IfStateChanges.accept (stateful, (Stateful s) -> applyLogicOnStateful (stateful), (Stateful s) -> doSomethingElse (stateful))

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

+0

похоже, что мы оказались на подобном уровне. великие умы. :-) –

Смежные вопросы