Simple and clear code – what does it look like? Basically, besides comprehensible clear naming we expect it to be highly granular and to have as little as possible branching. Indeed, such code is easy to read, to understand its purpose and required test coverage is optimal.
Well, clear naming has nothing special to talk about. High granularity is all about code decomposition (The Single Responsibility principle can also be applied here).
One of frequent reasons for the code branching is a ramification of logic depending on some object’s internal state (normally, it is a domain class object). Such branching can be “switch”-style – when you compare a particular state variable with its possible values. However, a more common and basic is the binary branching, which operates on boolean conditions. E.g., if object is in this state then do this else do that.
Often there is a need to repeat such logic in many places throughout the application code. Additionally, such logic may happen to be more intricate than one conditional if statement. To simplify it one may define appropriate isXXX() method(s) to the examined object’s class. But what if the object state is fairly complex or domain class is not aware of its possible states, or the logic examining its state is not limited to several cases, but more cases can be developed in future. Or even, you can be trapped in a situation, when you are not allowed to change the domain object class.
So here is an elegant solution… it’s the Matcher pattern! This is a pretty simple design pattern that consists of three classes in general case. The pattern’s class diagram is following:
There are the Entity class which is the specific domain class, the Matcher which is an abstract class or interface with a single abstract method that represents the matching algorithm, and the ConcreteMatcher which is a specific implementation of Matcher.
When implementing the pattern, Java generics can be leveraged to provide more flexibility with less code.
public interface Matcher
{
boolean matches
(T object
);
}
public class EuropeanCountryMatcher
implements Matcher
<Country
> {
// the list of EU countries – can be injected
private Set countryCodes
;
// Checks if the given country is an EU country
public boolean matches
(Country country
) {
return (countryCodes.
contains(country.
getCode()));
}
}
What is even more beautiful about the Matcher is that it can be injected into classes that rely on its logic. E.g.
public class DeliveryStrategy {
private Matcher<Country> specialPartnerCountryMatcher;
public void setSpecialPartnerCountryMatcher(Matcher<Country> matcher) {
specialPatnerCountryMatcher = matcher;
}
public Priority getDeliveryPriorityForClient(Customer customer) {
if (specialPatnerCountryMatcher.matches(customer.getAddress().getCountry())) {
return Priority.HIGHEST;
} else {
return Priority.STANDARD;
}
}
}
Besides logic reuse and simplification, another nice use of Matcher is the collection filtering.
I believe, similar “Matcher” approaches have been used in many projects independently. One described here can have pretty big number of various applications, so is worthy of being stated as a design pattern.