Skip to content

Instantly share code, notes, and snippets.

@fourgates
Last active September 11, 2020 20:23
Show Gist options
  • Save fourgates/db5168902b83284e48aa047467d9a54a to your computer and use it in GitHub Desktop.
Save fourgates/db5168902b83284e48aa047467d9a54a to your computer and use it in GitHub Desktop.

SOLID is an acronym for several of the most useful OOP design principles:

In this post, I will cover the first principle, the Single Responsibility Principle (SRP). I hope these posts will give you an understanding of how these principles can improve the way you write code and maybe impress the next person that reviews your code!

Single Responsibility Principle

"A class should have only one reason to change"

-Robert C. Martin

A class should encapsulate responsibility. A simple way to implement this is to group together code that is going to change for the same reason. This allows us to write modular code that is easily testable and maintainable.

Another reason for learning this principle is to learn how and when to refactor code. Take a look at your source control and find the top five most touched files. There is a good chance these classes are breaking the Single Responsibility Principle. If these areas are constantly changing or, worse yet, frequently creating bugs this may be a great place to apply this principle.

git log --pretty=format: --name-only | sort | uniq -c | sort -rg | head -10

Single

What does "single" mean? Stop right there. This does not mean you should write one method classes! This simply means all the code in a module should generally have the same theme or reason to change. Let's review a simple example:

public class Car{
  private Engine engine;

  public void start(){
    this.engine.start();
  }
}

This is an excellent demonstration of the SRP. We can start the Car without having to know anything about the Engine. Starting the Engine is abstracted to its own class. We should be able to make changes to the Engine class without changing how the Car starts.

Code Smell - Overly Coupled Classes

// Good
public Class A{
  @Autowired
  public A(B bService){
  }
}

// Potentially Bad
public Class A{
  @Autowired
  public A(B serviceB, C serviceC, ... Z serviceZ){
  }
}

If a class has an abundance of dependencies it can't possibly have a single responsibility! A common symptom of "single" is having common dependencies. If you have class B and class A requires class B to do work these classes are considered coupled. Now say class A needs classes B-Z to do all its work, this could be a potential problem. This is often a red flag, so keep an eye out!

Code Smell - God Classes

A "God Class" is an object that controls too many other objects in a system and has grown beyond all logic to become, The Class That Does Everything.

If a class is several thousand lines long this is often a terrible code smell. This often indicates that the class is overly complex. However, keep in mind this can sometimes be justified. It is recommended to split these classes apart to become more object-oriented. Breaking apart these classes will not only reduce complexity but make your code more maintainable and testable. This is an implicit side effect of using SRP!

Pro Tip: When refactoring a God Class start with abstracting dependencies to a new class and take the dependent code with it!

Bad Example - God Class

/**
* this is a short example of what a "God" class CAN look like. 
* this is not always the case but in enterprise applications, the business logic
* for different operations can grow out of hand. This can lead to coupled code
* that becomes a commonplace for broken tests and bugs
*/
public class TeamGodClass{
  // create team
  public ResponseObj createTeam(){...}  
  public ResponseObj validateForCreate(){...}  
  
  public ResponseObj createBusinessLogic1(){...}    
  public ResponseObj createBusinessLogic2(){...}    
  public ResponseObj createBusinessLogic3(){...}    
  public ResponseObj createBusinessLogic4(){...}      
  
  // update team
  public ResponseObj updateTeam(){...}  
  public ResponseObj validateForUpdate(){...}      
  
  public ResponseObj updateBusinessLogic1(){...}    
  public ResponseObj updateBusinessLogic2(){...}    
  public ResponseObj updateBusinessLogic3(){...}    
  public ResponseObj updateBusinessLogic4(){...}     
  
  // mix / match
  // isNewObj -- this is a code smell!
  public ResponseObj createOrUpdateBusinessLogic(Boolean isNewObj){...}
}

Good Example - No God Class

/**
*  base class
*  this could also be a static helper class 
*/
public class GoodTeamClass{    
  // common logic needed in update and create
  protected ResponseObj commonBusinessLogic(){...}
}
/**
*  Class responsible for creating teams. 
*/
public class GoodCreateTeamClass extends GoodTeamClass{
  // create team
  public ResponseObj createTeam(){...}  
  public ResponseObj validateForCreate(){...}  
  
  public ResponseObj createBusinessLogic1(){...}    
  public ResponseObj createBusinessLogic2(){...}    
  public ResponseObj createBusinessLogic3(){...}    
  public ResponseObj createBusinessLogic4(){...}         
}
/**
*  Class responsible for updating teams
*/
public class GoodUpdateTeamClass extends GoodTeamClass{    
  // update team
  public ResponseObj updateTeam(){...}  
  public ResponseObj validateForUpdate(){...}      
  
  public ResponseObj updateBusinessLogic1(){...}    
  public ResponseObj updateBusinessLogic2(){...}    
  public ResponseObj updateBusinessLogic3(){...}    
  public ResponseObj updateBusinessLogic4(){...}     
}

Bad Example - Coupled DAO Service

/**
*  This is an example of a class doing more than it needs to
*  This class should handle Team DAO logic
*  Another class should handle TeamPlaer DAO logic 
*/
public class BadTeamService{

  public ResponseObject saveTeam(Team team, List<TeamPlayers> teamPlayers){
    validateTeam(team); // throw TeamValidationException extends RuntimeException if invalid
    validateTeamPlayers(teamPlayers);
    
    // business logic for updating team and its players
    insertOrUpdateTeam(team);
    insertOrUpdateTeamPlayers(teamPlayers);
  }
}

Good Example - Decoupled DAO Service

/**
*   This is a better implementation to maintain single responsibility between DAO entities
*/
public class GoodTeamService{
  GoodTeamPlayerService goodTeamPlayerService = new GoodTeamPlayerService();
  
  public ResponseObject saveTeam(Team team, List<TeamPlayers> teamPlayers){
    validateTeam(team); // TeamValidationException extends RuntimeException
    
    // business logic for updating team and its players
    insertOrUpdateTeam(team);
    goodTeamPlayerService.saveTeamPlayers(teamPlayers);
  }
}
public class GoodTeamPlayerService{

  public ResponseObject saveTeamPlayers(List<TeamPlayers> teamPlayers){
    validateTeamPlayers(teamPlayers); // TeamValidationException extends RuntimeException
    
    // business logic for updating team and its players
    insertOrUpdateTeamPlayers(teamPlayers);
  }
}

Conclusion

Once you are done refactoring, make sure you take time to reflect and ensure these changes are an improvement to maintainability. You should design your classes to be easily manageable and encourage ease of use. While God classes also have their place in software development, they typically do not have either trait.

Writing code is a true art form. It’s very similar to how a musician writes music or how an artist paints a portrait. Becoming a great artist takes time and practice. It takes making mistakes, learning what doesn’t work, and how to go about solving problems. You need to learn from blunders and be able to recognize when you’ve faced a problem you have already solved. You should be able to model solutions based on these problems. Sometimes you need to think outside the box, while other-times keeping things simple is often the best solution.

If you liked this post feel free to follow me! I would like to take you on a journey of becoming the best you can possibly be.

Resources

https://stackify.com/solid-design-principles/

https://www.toptal.com/software/single-responsibility-principle

https://hackernoon.com/you-dont-understand-the-single-responsibility-principle-abfdd005b137

https://en.wikipedia.org/wiki/Active_record_pattern

https://softwareengineering.stackexchange.com/questions/150760/single-responsibility-principle-how-can-i-avoid-code-fragmentation

https://codeburst.io/understanding-solid-principles-single-responsibility-b7c7ec0bf80

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment