The SRP and Profile class
Learn the importance and implementation of the Single Responsibility Principle (SRP) in profile class.
Writing unit tests isn’t an exercise that occurs in a vacuum. It’s instead part of the larger, continually shifting puzzle we call design. Our system’s design impacts our ability to write tests and vice versa.
We’ll also investigate command-query separation, which uses methods that don’t end up fooling their users by both creating side effects and returning values.
We’ll apply these principles by refactoring code in the Profile
class.
Let’s take a look at our Profile
class so far:
package iloveyouboss; import java.util.*; import java.util.function.*; import java.util.stream.*; public class Profile { private Map<String,Answer> answers = new HashMap<>(); private int score; private String name; public Profile(String name) { this.name = name; } public String getName() { return name; } public void add(Answer answer) { answers.put(answer.getQuestionText(), answer); } public boolean matches(Criteria criteria) { calculateScore(criteria); if (doesNotMeetAnyMustMatchCriterion(criteria)) return false; return anyMatches(criteria); } private boolean doesNotMeetAnyMustMatchCriterion(Criteria criteria) { for (Criterion criterion: criteria) { boolean match = criterion.matches(answerMatching(criterion)); if (!match && criterion.getWeight() == Weight.MustMatch) return true; } return false; } private void calculateScore(Criteria criteria) { score = 0; for (Criterion criterion: criteria) if (criterion.matches(answerMatching(criterion))) score += criterion.getWeight().getValue(); } private boolean anyMatches(Criteria criteria) { boolean anyMatches = false; for (Criterion criterion: criteria) anyMatches |= criterion.matches(answerMatching(criterion)); return anyMatches; } private Answer answerMatching(Criterion criterion) { return answers.get(criterion.getAnswer().getQuestionText()); } public int score() { return score; } public List<Answer> classicFind(Predicate<Answer> pred) { List<Answer> results = new ArrayList<Answer>(); for (Answer answer: answers.values()) if (pred.test(answer)) results.add(answer); return results; } @Override public String toString() { return name; } public List<Answer> find(Predicate<Answer> pred) { return answers.values().stream() .filter(pred) .collect(Collectors.toList()); } }
Example: Profile
class and SRP
At under a hundred source lines, Profile
isn’t inordinately large and doesn’t seem excessively complex. It does contain some hints that the class exhibits less-than-ideal design, though.
-
The
Profile
class tracks and manages information for a company or person, including a name and a collection of answers to questions. This set of information that theProfile
class captures will most likely need to change over time. More information will ...