The Single Responsibility Principle

The Single Responsibility Principle

A class should have one, and only one, reason to change.

– Robert C. Martin

When we talk about reasons to change we are talking about business responsibilities. Pieces of business logic. Each class should deal with one singular piece of business logic. It should do one thing well and have only a singular reason to be changed.

One example of this would be creating an Orders Report class that gathers company order data, calculates and processes it, and finally formats it before presenting it back to the classes consumer. These are multiple responsibilities and multiple reasons to change.

Consider this reporting class.

This class has several reasons to change based on unrelated business considerations.

If the sales department changes the way they want to present the data, for example in a spreadsheet, the class will need to change. The same thing if a DBA makes a change to the data store’s structure or location, the class will have to change as well.

We clearly have two reasons to change: one is business logic-related and the other is infrastructure-related. According to the Single Responsibility Principle, these concerns shouldn’t reside in the same object.

Let’s look at a refactored version.

First, we inject our data access repository object into the reporting class, removing that responsibility and that reason to change. Ideally, we would type-hint an interface but we will save that for a future discussion. Next, we pass in an output formatting object, once again removing business logic and responsibility.

We can now change the data store and output formatting at will and the OrderReport class doesn’t care. It is now easy to maintain, scale, and most importantly test.

For example, simply mock a DataRepository and an OutputFormatter and test the report classes’ data processing and calculation functionality in isolation.

The Takeaway

This applies to all entities in your coding, not just classes. From Packages to Classes and Functions each entity in your code should have a single responsibility. To do one thing, do it well, and move along.

Always ask yourself whether the logic you are introducing should live in this class or not. Is this a different or unique responsibility? What outside factors could force a change to it? How does it add to the complexity of the class?

When in doubt try writing some basic comments that map out and describe what the code you are implementing will be doing. If you find yourself using a lot of statements like “in this case”, “but if”, “except when”, “or else this” then the method or class will be probably too complex. This is a perfect opportunity to break things out and simplify your code.