https://www.theserverside.com/tip/How-to-apply-the-single-responsibility-principle-in-Java
The single responsibility principle in Java demands that a class serves a single, clear purpose.
Any attempt to add peripheral functionality to a well-designed class must be thwarted. Instead, you must place it into another Java component.
The single responsibility principle (SRP) represents the first letter in the object-oriented design pattern acronym SOLID, which is composed from the following terms:
Software components built according to the SRP will accrue benefits in the following 10 domains:
Here's a simple example of a Java class that correctly observes the single responsibility principle. Imagine a number guessing game where each time it is played the game tracks two things: what the magic number was, and the number of attempts to guess it.
public class GameResult { int guesses; int magicNumber;
GameResult( int numberOfGuesses, int theNumber) { guesses = numberOfGuesses; magicNumber = theNumber; }
}
This GameResult has a single responsibility that is clear and concise: Keep track of the results of a game.
Now imagine a developer wants to additionally keep track of the results of all games played. One way to achieve this is to add a List to the GameResult class and add to it with every creation of a new GameResult object.
public class GameResult {
static List<GameResult> history = new ArrayList();
int guesses; int magicNumber; GameResult( int numberOfGuesses, int theNumber) { guesses = numberOfGuesses; magicNumber = theNumber; history.add(this); } }
This code will work, but it's ugly.
The code also violates the single responsibility principle in Java. The GameResult component, which should do nothing more than keep track of a single game, is now also tracking history.
Currently the number-guessing-game code is manageable, but imagine if we need methods that cover several other functions, such as the following:
These methods don't serve the class's primary purpose, and will pollute the GameResult class. The result would morph into something like the following:
public class GameResult {
static List<GameResult> history = new ArrayList(); int guesses; int magicNumber; GameResult( int numberOfGuesses, int theNumber) { guesses = numberOfGuesses; magicNumber = theNumber; history.add(this); }
public static void printHistory(){ /* lots of code */ } public static void updateRow() { /* lots of code */ } public static void deleteRow() { /* lots of code */ } public static void editRow() { /* lots of code */ } }
In this example, to easily refactor the code and fix the single responsibility principle violation, just create a new class dedicated exclusively to manage the history.
This new class's single responsibility is to provide history-related functions.
public class GameHistory { private static List<GameResult> history = new ArrayList(); public static void printHistory(){ /* lots of code */ } public static void updateRow() { /* lots of code */ } public static void deleteRow() { /* lots of code */ } public static void editRow() { /* lots of code */ } }
As with all design patterns, there are drawbacks to weigh against the benefits.
When taken to the extreme, application of the single responsibility principle can result in extremely fine-grained micro-components that present the following challenges:
As software developers become more experienced with object-oriented principles and domain-driven design, the observation of SOLID principles becomes a natural part of the development process.
Over time, they will better understand which functions to group together in a single Java class, and which components to separate through an easily integrated interface.
When you correctly balance those factors, observation of the single responsibility model in Java makes enterprise systems faster to develop, easier to troubleshoot and cheaper to maintain over the long term.
The code for this single responsibility principle in Java example is available on GitHub.
30 Mar 2023