patternjavaMinor
Computing predicates of multiple items avoiding duplication of iteration code
Viewed 0 times
predicatesduplicationiterationitemsmultiplecodeavoidingcomputing
Problem
I am breaking my head with how to get rid of semantic duplication(Code that is syntactically the same but does different things).
I can't find anywhere a post or something that mentions a bit how to refactor this kind of duplication. All I found was this:
Duplicate Code and Ceremony in Java but it does not go into detail on how to refactor it.
This is the code that is causing me problem:
In these two methods there is semantic duplication, because both iterate a list and also check some condition/s. Any idea how could I make this semantic duplication disappear?
I would really appreciate some tip or suggestion on how to refactor this.
I can't find anywhere a post or something that mentions a bit how to refactor this kind of duplication. All I found was this:
Duplicate Code and Ceremony in Java but it does not go into detail on how to refactor it.
This is the code that is causing me problem:
public class TeamValidator {
public boolean isThereALeader(List team) {
Iterator iterator = team.iterator();
while(iterator.hasNext()) {
Member member = iterator.next();
String role = member.getRole();
if(role.equals("Leader"))
return true;
}
return false;
}
public boolean areThereAtLeast2NewJoiners(List team) {
int amountOfNewJoiners = 0;
for(Member member:team) {
if(amountOfNewJoiners == 2)
return true;
DateTime aMonthAgo = DateTime.now().minusMonths(1);
if(member.startingDate().isAfter(aMonthAgo)) {
amountOfNewJoiners++;
}
}
return false;
}
}In these two methods there is semantic duplication, because both iterate a list and also check some condition/s. Any idea how could I make this semantic duplication disappear?
I would really appreciate some tip or suggestion on how to refactor this.
Solution
Key here is to identify that both functions check whether there are at least n members that match a certain condition.
So we introduce a Matcher interface :
Which we then use to make a method
Method
So we introduce a Matcher interface :
public interface Matcher {
boolean matches(T candidate);
}Which we then use to make a method
hasAtLeastNMatches(). And both methods can be implemented by calling that.public class TeamValidator {
public boolean isThereALeader(List team) {
return hasAtLeastNMatches(team, Is.Leader, 1);
}
public boolean areThereAtLeast2NewJoiners(List team) {
final DateTime aMonthAgo = DateTime.now().minusMonths(1);
return hasAtLeastNMatches(team, Is.NewJoiner, 1);
}
private boolean hasAtLeastNMatches(Iterable members, Matcher condition, int minimumNumberOfMatches) {
int count = 0;
for (Member member : members) {
if (condition.matches(member) && ++count >= minimumNumberOfMatches) {
return true;
}
}
return false;
}
private static enum Is implements Matcher {
Leader {
@Override
public boolean matches(Member candidate) {
return candidate.getRole().equals("Leader");
}
},
NewJoiner {
@Override
public boolean matches(Member candidate) {
return candidate.startingDate().isAfter(DateTime.now().minusMonths(1));
}
}
}
}Method
hasAtLeastNMatches() can obviously also be reused ouside this class. In fact this kind of reuse is so common that several open source code projects offer these kinds methods and interfaces: e.g. Apache commons collections and Guava . In fact what I called Matcher they both call Predicate.Code Snippets
public interface Matcher<T> {
boolean matches(T candidate);
}public class TeamValidator {
public boolean isThereALeader(List<Member> team) {
return hasAtLeastNMatches(team, Is.Leader, 1);
}
public boolean areThereAtLeast2NewJoiners(List<Member> team) {
final DateTime aMonthAgo = DateTime.now().minusMonths(1);
return hasAtLeastNMatches(team, Is.NewJoiner, 1);
}
private boolean hasAtLeastNMatches(Iterable<Member> members, Matcher<Member> condition, int minimumNumberOfMatches) {
int count = 0;
for (Member member : members) {
if (condition.matches(member) && ++count >= minimumNumberOfMatches) {
return true;
}
}
return false;
}
private static enum Is implements Matcher<Member> {
Leader {
@Override
public boolean matches(Member candidate) {
return candidate.getRole().equals("Leader");
}
},
NewJoiner {
@Override
public boolean matches(Member candidate) {
return candidate.startingDate().isAfter(DateTime.now().minusMonths(1));
}
}
}
}Context
StackExchange Code Review Q#26642, answer score: 5
Revisions (0)
No revisions yet.