SOLID Principles – Single Responsibility Principle


a class should have only a single responsibility

This is my favorite principle of all. It is the easiest to violate and thus in my opinion, not respecting it is the most common issue I find when I review code. Many people I talk to, mistakenly think that the SRP is trying to keep classes short, but it is much more than that.

Let’s discuss an example. Imagine that we are writing an application for a classic store type business who specializes in selling construction material. We will probably end up having two classes like this.

class Order
{
	IEnumerable<OrderItem> Items { get; set; }
	ClientInformation ClientInfo { get; set; }
	ShippingInformation ShippingInfo { get; set; }
}

class ReportCreator
{
	// Adds an image to specific coordinates
	void AddImage() { }
	// Adds text to specific coordinates
	void AddText() { }
	// Adds table data to specific coordinates
	void AddTable() { }
}


We receive a fairly common requirement like, we need to print reports for an order. Now the question is

A: Should the Order class receive a function GenerateReport(ReportCreator repCreator)?B: Should the Report Creator receive a function AddOrder(Order order)

The answer is of course C, there is always option C. In the example above, the Order class should be responsible to hold every piece of information that defines a real life order. The ReportCreator class on the other hand, should be responsible of exposing basic capabilities that every entity that needs to build up a report could use. The solution is a third class, and its sole responsibility is to know how to convert an Order into a Report, which part of its information goes where.

Let’s consider another common requirement. Imagine that we need to calculate the total amount the buyer needs to pay. Our first instinct is to add the code to the Order class, since it’s containing everything that we need. So we proceed in adding a function that returns the amou…, WAAAIT! How the prince is calculated is a different responsibility. We don’t want to change the Order class every time the requirements for price calculation change.

I see this frequent mistake happen all the time, it’s not that the functionality wasn’t correctly written, it was not placed into the correct class. For me this simple sentence that defines this principle implies that each class has a purpose, this purpose if fulfilling a requirement, so I take the liberty to rephrase it into a less elegant sentence “I should only change this class if the requirement that it is responsible for changed”

Requirements change for many reasons, client changed his mind, architectural change influenced how information flows between classes, you just misunderstood them, etc. Keep also in mind that not every class fulfills a direct client requirement. A repository class changes if the team decides that they change the way that data is stored and queried from the database.

Here are a few tips how to be more mindful about this principle

  • Write class summaries: try to formulate a simple phrase (without using the word “and”), that will help developers who will work on this area later to know what its original purpose is.
  • Keep an eye out on class size: Indeed a huge class probably takes care of more responsibility than it should, consider splitting it into several.
  • Play the Devil’s advocate: if I have doubts that I’m doing the right thing in the right place I play out a little conversation in my head with the class who is resisting to change. Its asking me question like: “Why do I need to do this? This is not my responsibility? Can’t you find somebody else who could do this? Why are you touching me every damn time? Who gives a F… about a ReportCreator … I don’t want to know about it!
  • The dumber the better: The less a class know about other components the better it is. If a class is managing information flow between dozens of different classes, chances are it does more than it should.
  • Keep it clean: Every team member adheres to the status quo, if they see garbage they will not hesitate to add to the pile, if they are the first who break the harmony they will think twice.

Keep in mind, just because we can implement something in some class, it doesn’t mean we should.