By expanding (more like enhancing :) ) the filter pattern described here and borrowing some of the beauties of the matcher pattern in Guice, I coded a more concise version of this filter pattern. Now this pattern’s implementation has a fluent API for combining other filters together, a place where all filters can be defined, a way of negating filters, etc. Further in this post I will provide a simple example that describes how the newer version of this pattern can be utilize. But before showing the example, I will provide the pattern’s newer definition and implementation herein.
Filter pattern’s main interface…
public interface Filter <T> {
Filter <T> and(Filter <? super T> thing);
Iterable <T> filter(Iterable <T> thing);
boolean evaluate(T thing);
Filter <T> or(Filter <? super T> thing);
}
and its abstract implementation…
public abstract class AbstractFilter <T> implements Filter <T> {
public Filter<T> and(Filter<? super T> thing) {
return new AndFilter<T>(this, thing);
}
public Iterable<T> filter(final Iterable<T> thing) {
return new Iterable<T>(){
public Iterator<T> iterator() {
return new FilteringIterator<T>(
AbstractFilter.this,
thing.iterator()
);
}
};
}
public Filter<T> or(Filter<? super T> thing) {
return new OrFilter<T>(this, thing);
}
private static class AndFilter<T> extends AbstractFilter<T> {
private final Filter<? super T> one;
private final Filter<? super T> two;
AndFilter(
Filter<? super T> one,
Filter<? super T> two
) {
this.one = one;
this.two = two;
}
public boolean evaluate(T thing) {
return one.evaluate(thing)
&& two.evaluate(thing);
}
}
private static class OrFilter<T> extends AbstractFilter<T> {
private final Filter<? super T> one;
private final Filter<? super T> two;
OrFilter(
Filter<? super T> one,
Filter<? super T> two
) {
this.one = one;
this.two = two;
}
public boolean evaluate(T thing) {
return one.evaluate(thing)
|| two.evaluate(thing);
}
}
private static class FilteringIterator<T> implements Iterator <T> {
private final Filter <T> filter;
private final Iterator <T> base;
private T next;
FilteringIterator(
Filter<T> filter,
Iterator<T> base
){
this.filter = filter;
this.base = base;
tryNext();
}
private void tryNext() {
next = null;
while (base.hasNext()) {
final T item = base.next();
if (item != null && filter.evaluate(item)) {
next = item;
break;
}
}
}
public boolean hasNext() {
return next != null;
}
public T next() {
if (next == null) throw new NoSuchElementException();
final T returnValue = next;
tryNext();
return returnValue;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
Now that I have provided this pattern’s definition and implementation, I will proceed with a simple example of its use. This example will deal with the Java Reflection API. Imagine that you are trying to query all the methods in a class which names start with “eq”, “has”, “toS” and have the Object class as a parameter. Most of the times you will have multiple IFs checking the methods of your interest. What if I don’t want to do that and instead, I want to use the Filter design pattern. Well, in order to do that all you need to code are two basic filters: one for looking at the methods’ names and the other one for looking at the methods’ parameters types.
public static Filter <Method> containedParameters(final Class<?>... params){
return new AbstractFilter <Method>(){
public boolean evaluate(Method thing) {
final List<Class<?>> s = Arrays.asList(
thing.getParameterTypes()
);
for(Class<?> each : params){
if(s.contains(each)){
return true;
}
}
return false;
}
};
}
public static Filter <Method> startedWith(final String... prefixes) {
return new AbstractFilter <Method>(){
public boolean evaluate(Method thing) {
for(String prefix : prefixes) {
if(thing.getName().startsWith(prefix)){
return true;
}
}
return false;
}
};
}
Simple, huh? totally!
Well, what if you want to negate one of those filters? Well, this is even simpler to implement:
public static <T> Filter <T> not(final Filter <T> filter) {
return new AbstractFilter <T>(){
public boolean evaluate(T thing) {
return !filter.evaluate(thing);
}
};
}
And to finalize this post, here is how to put these filters to work:
public class RunPattern {
private RunPattern(){}
public static void main(String... args) {
// calling filters one after another
for(Method each : containedParameters(Object.class)
.filter(startedWith("eq", "has", "toS")
.filter(Arrays.asList(Object.class.getDeclaredMethods())))){
System.out.println(each.getName());
}
// or maybe you can start using and & or operators
final Filter <Method> m1 = containedParameters(Object.class)
.and(startedWith("eq", "has", "toS"));
for(Method each : m1.filter(Arrays.asList(Object.class.getDeclaredMethods()))){
System.out.println(each.getName());
}
// which method do you you prefer?
// it will be up to you...
// hmmm....what if?
final Filter <Method> m2 = not(containedParameters(Object.class))
.and(startedWith("eq", "has", "toS"));
for(Method each : m2.filter(Arrays.asList(Object.class.getDeclaredMethods()))){
System.out.println(each.getName());
}
}
}
Hopefully you will find this newer version of this pattern useful. Ciao!