2016-05-13 62 views
0

我试图使Predicate为了知道我是 已完成某些记录,但我需要一个可能随时更新的记录的外部列表;我也需要跟踪我已经处理的那些。称为lambda表达式,但取决于“外部”参数

这是怎样的代码看起来像:

// This one is at class level and gets updated in different places 
Set<String> recipients ... 

private void method(Iterable<CustomType> records) { 
    final Set<String> doneWith = new HashSet<>(); 

    try { 
    someService.doThis(records, record -> { // records is a list of CustomType 
     boolean notify = Stream.of(doneWith, recipients) 
     .noneMatch(s -> s.contains(record.getField())); 

     if (notify) { 
     doneWith.add(record.getField()); 
     } 
     return notify; 
    }); 
    recipients.addAll(doneWith); 
    } catch (Exception e) { 
    // TODO: Do something here 
    } 
} 

...现在我要“外化”为Predicate第二个参数someService.doThis(records, <this_one>。任何线索?

这是呼叫的类型:

public void doThis(Iterable<CustomType> records, Predicate<CustomType> notify) { 
    records.forEach(r -> { 
    // Do some stuff here 
    if (notify.test(r)) { 
     // Do some more stuff here 
    } 
    }); 
} 

注:主要原因希望能够重用是因为我可能需要在别处的谓语,但截至目前,它也使用在测试案例中。

+0

呃,这是什么部分是'Predicate'?而谓词确实不应该修改外部状态,就像你在'doneWith'中做的那样。但是你真正需要做的只是返回一个返回一个'Predicate'的方法,并从中返回这个lambda。 –

+0

所以你的意思是说''使用'doneWith'不应该在'Predicate'中完成? –

+1

是的,我的意思是说。 –

回答

0

据我所知,你的功能其实只是:

record -> !recipients.contains(record.getField()) 
      && doneWith.add(record.getField()) 
0

我认为你需要使用BiPredicate作为参数。

public void doThis(Iterable<CustomType> records, BiPredicate<CustomType, Iterable<CustomType> > shouldNotify) { 
    final Set<String> doneWith = new HashSet<>(); 
    records.forEach(r -> { 
    if (shouldNotify.test(r, doneWith)) { 
     // Do something here 
    } 
    }); 
} 
0

如果你只想外部化您的谓词,那么你只需要做出一个新的谓词:

新建谓词:

@Builder 
public class MyPredicate implements Predicate<CustomType> { 
    private Set<String> doneWith; 
    private Set<String> recipients; 

    @Override 
    public boolean test(CustomType record) { 
     boolean notify = Stream.of(doneWith, recipients) 
       .noneMatch(s -> s.contains(record.getField())); 

     if (notify) { 
      doneWith.add(record.getField()); 
     } 
     return notify; 
    } 
} 

新通话:

private void methodName(Iterable<CustomType> records) { 
    someService.doThis(records, MyPredicate.builder().doneWith(doneWith).recipients(recipients).build()); 
    recipients.addAll(doneWith); 
} 

继续解决你的问题我虽然这可能是有帮助的。你的谓词应该基本上只做测试。您正在谓词中进行更改操作。如果移动并做出一些改变,你可能需要改变你的代码的结构有点:

新建谓词:

@Builder 
public class MyPredicate implements Predicate<CustomType> { 
    private Set<String> listToFind; 

    @Override 
    public boolean test(CustomType record) { 
     return listToFind.parallelStream() 
       .noneMatch(s -> s.contains(record.getField())); 
    } 

} 

你新的呼叫:

private void methodName(Iterable<CustomType> records) { 
    someService.doThis(records, 
      MyPredicate.builder().listToFind(recipients).build() 
        .and(MyPredicate.builder().listToFind(doneWith).build()) 
    ); 
    recipients.addAll(doneWith); 
} 

最后你新服务:

// TODO: Please consider using injection for your doneWith Set 
final Set<String> doneWith = new HashSet<>(); 

public void doThis(Iterable<CustomType> records, Predicate<CustomType> shouldNotify) { 
    records.forEach(r -> { 
     if (shouldNotify.test(r)) { 
// TODO: You continue here... 
      doneWith.add(r.getField()); 
     } 
    }); 
} 

所以,你现在有一个谓词这实际上是一个条件的两个谓词。这样你就知道只有当所有的套件都缺少你的customType时,你才可以添加一个元件到你的doneWith集。您的服务需要共享此套件,并且最好的方法是使用上下文相关注入。如果你这样做,你不需要改变你的代码,你会有一个通用的Predicate,你可以使用一般的和外部的。如果至少有两个输入变量不是这种情况,那么这个想法将起作用。