Today I will explore a little more about two patterns and how they can be used together to achieve an expressive way of doing stuff (I'll explain what I mean with this). The exercise of today was inspired in a discussion I've got at work about how we could use decorators to implement filters. First of all a filter is a mechanism by which you select a subset of a bigger set. In Java semantics if you got a Collection then a filter is a transformation that will enable you to create a Collection B from an initial Collection A. Where, of course, we got that B as a sub collection of A. in Java language the contract should be something like this
public static <T> Collection<T> filter(
Collection<T> bagOfStuff
)
But wait. Weren't we talking about fancy stuff as decorator and factory patterns? Well in a way, yes. See, this is the simple way of doing things. If you need to filter something you just implement a method like that. The problem is that the filter mechanism is a very common feature but, on the other hand, the way we filter as well as the collections we act upon are very diverse. If we approach things like this we will rapidly end in the following jungle
public static Collection<Integer>
filterEvenNumbers(
Collection<Integer> bagOfIntegers
)
public static Collection<Integer> filterNegativeNumbers(
Collection<Integer> bagOfIntegers
)
public static Collection<String> filterNotNullOrEmpty(
Collection<String> bagOfStrings
)
public static Collection<String> filterNotNulOrEmpty(
Collection<String> bagOfStrings
)
Well I believe that you figure out what is wrong with this right? And no the problem is not java and scala is the solution. No, not for this problem at least. The problem here is that we are duplicating always the same logic when the only thing that changes is the criteria. So you already figure it out right? We need to convert this mechanism in a more generic one. How? Well maybe if we add another field specifying the criteria maybe would get rid off with this letter soup duplication. Something like this?
public static <T> Collection<T> filter(Collection<Filter>, Predicate<T> predicate);
Here predicate is basically a contract, or in Java semantics an interface, as the following
public interface Predicate<T> {
boolean verify(T element);
}
But.. wait, again, what the f%c& do decorators and factories relate with this whatsoever? The answer is composition and verbosity respectively. See, the mechanism above is not generic enough for composition because most of the times we don't want just to filter by one criteria but we have the need to compose several ones. For example, maybe you are not interested in see if a String is just not null or empty, maybe what you really want is a way to find if the String is not null or empty and, additionally if it is a palindrome. With the previous mechanism you would end up with the need to invoke the filter two times, so something like this
filter(filter(bagOfStrings,new Predicate<String>{
@Override
public boolean verify(String word) {
return word==null || word.isEmpty();
}
}),new Predicate<String>{
@Override
public boolean verify(String word){
int i1 = 0;
int i2 = word.length() - 1;
while (i2 > i1) {
if (word.charAt(i1) != word.charAt(i2)) {
return false;
}
++i1;
--i2;
}
return true;
}
})
What's wrong with this? Well nothing actually because it would run and do that you meant for. But it is not a very nice way to do things is it? First of all the anonymous classes need to be implemented again, the next time we need the same kind of filter. But the worst is that we end up with the feeling that this is not right because we should do only one filter operation and not several since we got a very simple predicate, we just want those words that are (not null or empty ) and palindrome. See this is just one criteria. Is it? Well actually what whe are doing is logical composition. And this lead us to the other pattern. The decorator. With the decorator pattern we envelop the call of a type T with another object of the same type. This is equivalent of composition of mathematical operations, with the restriction that the function follow the structure
f: T --> T, where T is some kind of data type
And how do we implement this? Here you got an example of a base class has the common structure we talked about
public abstract class PredicateBase<T> implements Predicate<T> {
protected Predicate<T>[] decorators;
public PredicateBase(Predicate<T>... decorators){
this.decorators=decorators;
}
public PredicateBase{}
public abstract boolean verify(T element);
}
Not much advantage for now right? But wait and lets concentrate first in getting rid of the previous anonymous classes. We could do something like this
public static class
IsEmptyOrNull extends PredicateBase<String>{
@Override
public boolean verify(String element) {
return element==null || element.isEmpty();
}
}
public static class
IsPalindrome extends PredicateBase<String>{
@Override
public boolean verify(String element){
int i1 = 0;
int i2 = element.length() - 1;
while (i2 > i1) {
if (element.charAt(i1) != element.charAt(i2)) {
return false;
}
++i1;
--i2;
}
return true;
}
}
With this new approach all we could do now is just invoke new instances and the logic would be separated from the filter, much more clean right?
filter(
filter(
bagOfStrings,
new Not(new IsEmptyOrNull)
)
,new IsPalindrome
)
Now you are saying but you are cheating man, you are using some magical Not object to negate the predicate and that don't exists well it exists now
public static class Not<T> extends PredicateOperator<T>{
public Not(Predicate<T>... decorated){
super(true,decorated);
}
@Override
public boolean operation(
boolean current,
Predicate<T> predicate, T element
){
return current && !predicate.verify(element);
}
}
But you are cheating again man, where the fuck is the PredicateOperator. Gladly you asked! See, the PredicateOperator is just another kind of operator that we can represent as follows
public static abstract class
PredicateOperator<T> extends PredicateBase<T>{
protected final boolean initialStatus;
public PredicateOperator(
boolean initStatus,
Predicate<T>... decorators
){
super(decorators);
this.initialStatus=initStatus;
}
public abstract boolean operation(
boolean current,
Predicate<T> predicate, T element
);
public boolean verify(T element){
boolean status=initialStatus;
for(Predicate<T> p : this.decorators){
status = operation(status,p,element);
}
return status;
}
}
that basically encapsulates the iterating of several predicates. And with this you can, in an abstract way, represent boolean operations based on predicates. Some more example follows
public static class
Or<T> extends PredicateOperator<T>{
public Or(Predicate<T>... decorated){
super(false,decorated);
}
@Override
public boolean operation(
boolean current,Predicate<T> p, T element
) {
return current || p.verify(element);
}
}
public static class
And<T> extends PredicateOperator<T>{
public And(Predicate<T>... decorated){
super(true,decorated);
}
@Override
public boolean operation(
boolean current,
Predicate<T> p,
T element) {
return current && p.verify(element);
}
}
Now that you see the purpose of the decorator pattern we can move into the factory one. See, all these predicates and operators (which are also predicates) don't change much. They don't hold state. They are pretty much final. So all the verbosity of creating instances and passing parameters could be reduced if we create those animals in the first place and then invoke them when needed. That's pretty much what a factory is
public class PredicateFactory {
public final static
Predicate<Integer> IS_EVEN = new IsEven();
public final static
Predicate<Integer> IS_POSITIVE = new IsPositive();
public final static
Predicate<String> IS_EMPTY_OR_NULL = new IsEmptyOrNull();
public final static
Predicate<String> IS_PALINDROME = new IsPalindrome();
public static <T> Predicate<T> And(
Predicate<T>... predicates
){
return new And(predicates);
}
public static <T> Predicate<T> Or(
Predicate<T>... predicates
){
return new Or(predicates);
}
public static <T> Predicate<T> Not(
Predicate<T> ...predicates
){
return new Not(predicates);
}
}
This way the initial code for filter the not empty and not null palindrome words could be reduced to a simple call like this one
filter(bagOfWords,And(Not(IS_EMPTY_OR_NULL),IS_PALINDROME))
You can see more examples here and the full source code here