Single Responsibility Principle

S in the SOLID principles represent single responsibility principle. SOLID is one the most praised set of principles developers should follow when they are working in OOP paradigm. As the name suggests every class should have one and only one responsibility to achieve, but why?

You can investigate following examples easier at my github page :

github link

public class SomeFinanceThing {
   String dbServerUrl = ...;
   String dbServerPass = ...;
   String dbName = "SomeFinanceThingDb";
   SomeDatabaseThing db;

   // Object specific fields
   ...
   ...

   public SomeFinanceThing(String dbServerUrl, ...){
      this.dbServerUrl = dbServerUrl;
      ...
      ...

      SomeConnectionObject conn = new SomeConnectionObject(dbServerUrl, dbServerPass);
      try{
        db = conn.connect(dbName);
      }
      catch(SomeDbConnException dbConnEx){
      // do some logging
      // return some feedback
      // etc
      application.quit();
      }
    }
    public void SaveSomeFinanceData(String data1, String data 2,...){
        SomeDbFriendlyObject dbFriendly = new Some...;
        db.save(dbFriendly);
    }
    public void DeleteSomeFinanceData();
    public SomeObject FindSomeFinanceData();
    // etc
}
public class SomeOtherFinanceThing {
   String dbServerUrl = ...;
   String dbServerPass = ...;
   String dbName = "SomeOtherFinanceThingDb";
   SomeDatabaseThing db;

   // Object specific fields
   ...
   ...

   public SomeOtherFinanceThing(String dbServerUrl, ...){
      this.dbServerUrl = dbServerUrl;
      ...
      ...

      SomeConnectionObject conn = new SomeConnectionObject(dbServerUrl, dbServerPass);
      try{
        db = conn.connect(dbName);
      }
      catch(SomeDbConnException dbConnEx){
      // do some logging
      // return some feedback
      // etc
      application.quit();
      }
    }
    public void SaveSomeFinanceData(String data1, String data 2,...){
        SomeDbFriendlyObject dbFriendly = new Some...;
        db.save(dbFriendly);
    }
    public void DeleteSomeFinanceData();
    public SomeObject FindSomeFinanceData();
    public SomeObject SomeOtherMethod();
    // etc
}

We handled database connection, we created a business object, converted it into a database object, write CRUD methods all the same time. Lets says we had 20 different objects we want to work with, we had to repeat same logic over and over again while we only cared about a few different fields. But thats not the only issue.

What if we want to change the server we are connecting to ? Then we would have to change 20 different classes, what if we decided to write backup copy to files on the disk, we would have to implement it in 20 different classes. What if we change the server password for some reason ? We would have to change it in all the class.

Every single change we want to achieve had to be replicated, every chance we might introduce a bug is also multiplied. If we want to change how we connect to a database we probably would have to rewrite our entire application.

public class DbConnectionHelper {
   String dbServerUrl = ...;
   String dbServerPass = ...;
   SomeConnection conn;

   public DbConnectionHelper(String dbServerUrl, ...){
      this.dbServerUrl = dbServerUrl;
      ...
      ...
      conn = new SomeConnectionObject(dbServerUrl, dbServerPass);
   }
   public Optional<SomeConnectionThing> getConnection(string dbName){
       return conn.connect(dbName);
   }
}
public class ObjectConverter<T, R> {
   SomeOtherHelperObject obj = ...;

   public ObjectConverter(...){
   ...
   }
   
   public R convert(T type){
   // some logic
   // some logic
   // some logic
   }
}
public interface CRUD<T> {
   public void save(T o);
   public void delete(T o);
   ...
}
public class FinanceObject1 {
    // fields

    // constructor
}
public class FinanceObject1Repository implements CRUD<FinanceObject1>{
   Optional<SomeConnectionThing> db;
   public FinanceObject1Repository(DbConnectionHelper helper, string dbName){
       db = helper.getConnection(dbName);
   }
   public void save(Data data1, Data data2, ...)
   {
       FinanceObject1 financeObject = new FinanceObject1(data1, data2, ...);
       ObjectConverter<FinanceObject1, DbFriendlyObject> converter = new Object Converter();
       db.save(converter.convert(financeObject));
   }
}

Much better already. Changes and bugs! are already confined within much smaller classes easier to handle, and code duplication reduced greatly. You don’t have repeat lots of code for each entity. But don’t think what i wrote above is anything optimal, you can make it better (and you should), you can create a generic repository and avoid rewriting CRUD methods for each repository.

But you should avoid creating classes for each CRUD method (create, read, update, delete), handling crud can be considered as a single responsibility because if you try to seperate those you will complicate your code more instead of simplifying. Rule of tumb is every class only needs one reason to be changed as stated in wikipedia by creator of SOLID principles Robert C. Martin.

Conclusion : by restricting each of our classes to have a single responsibility, we avoid duplicating code, exponentially increasing complexity, we reduce chance to introduce new bugs, make our code more readable and easier to extend and maintain all in once just by following a very simple principle.

Thank you for reading this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s