The Single Responsibility Principle: Identifying Violators
Jul 7 2016
Realizing that a class is violating The Single Responsibility Principle tends to be much more difficult that resolving the violations.
Here are a few techniques that you can use to help.
1. Simple summaries
Use a single sentence to summarize the responsibility of your class. If your summary contains a conjunction like “and” or “or”, then it is likely the class is violating SRP.
2. Use the comments/documentation
This is the same as #1, but apply it to the summaries in the documentation or comments.
3. List reasons that may cause change
For an example, see my previous post where I made a list of reasons that the BoardGame
implementation could change. These reasons do not need to be 100% realistic either.
Imagine you have implemented a game of Tic-tac-toe in your favorite language. Let’s say you have built a Game
abstraction, a Board
abstraction, and a Player
abstraction. The rules are exercised within the Game
. Everything is working working great, and you don’t know of any bugs in your implementation.
Now, what would need to change about your Game
if the rules of Tic-tac-toe suddenly changed, and it was now played with 3 players instead of 2?
What needs to change? What would be unaffected?
If there is a group of methods that need to change, or do not need to change, it may indicate that the class has an additional responsibility.
4. Look for method prefixes
Another indication that you might be violating SRP is if you find multiple methods that have a common prefix.
Take this CardGame
example.
interface CardGame {
int scoreHighestCard();
int scoreLowestCard();
Hand deal();
void shuffle();
}
Two of the methods have a prefix of score
. Classes with with a single responsibility, with high cohesion, do not tend to require a common prefix to communicate behaviour.
This is our clue that our class may have multiple responsibilities, or more optimistically, a clue that a new class is ready to be born!
What we can do here is break off the “score” methods on to a new Scorer
class.
interface Scorer {
int getHighest();
int getLowest();
}
And we might also rename the original now that its role in the system is much more clear.
interface Dealer {
Hand deal();
void shuffle();
}
5. Audit the abstraction level of your methods
A class with a single responsibility should have all methods at the same level of abstraction.
Here is another example of an SRP violation.
interface UI {
String read();
void write(String);
void update()
String prompt(String);
void message(String);
}
The read
and write
methods are reading and writing raw text to and from the screen, where the other three methods are responsible for interacting with the user.
Reading and writing is a lower level of abstraction. These are operations that the UI
class will use to implement its other methods, but reading and writing don’t match the abstraction level of the surrounding methods.
This mismatch in abstraction level could indicate an SRP violation.
Wrap up
These are some techniques that I have used to audit the responsibilities of a class. None of these are absolute. Having a group of methods with a common prefix is not going to illuminate an additional responsibility 100% of the time, but hopefully these techniques are more helpful than not the majority of the time.
Recent Articles
- Apprenticeship Retro Oct 14 2016
- What the hell is CORS Oct 13 2016
- Cross-site Tracing Oct 11 2016
- Learning Testing Tools First Oct 7 2016