Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Single Responsibility Principle Explained

DZone's Guide to

The Single Responsibility Principle Explained

As we delve into SOLID principles, we start with Singe Responsibility. It's not enough to consider the code - you have to look at the people involved as well.

· Java Zone
Free Resource

Bitbucket is for the code that takes us to Mars, decodes the human genome, or drives your next car. What will your code do? Get started with Bitbucket today, it's free.

Meet Ben and Tony, two typical employees of an e-commerce company called WooMinus. Ben is a crazy accountant, who can’t stop thinking about cents and euros, while Tony is a product owner who finds some peculiar joy in reports and wiki pages. Our two characters will be assisting us in our way deep into SOLID principles. We’ll start our journey with The Single Responsibility Principle.

Where’s My Money?!

Ben is going crazy today. Something bad happened to the tax calculation, and the company started paying the government way more money than necessary. He comes to you, slaps you in the face, and threatens that if you don’t find the cause of the problem in the next 10 minutes, he’ll report you to the CFO and make sure all your precious sites with sweet kitties are blocked by the company’s proxy forever. With that much at stake, you turn on your superpowers and find the issue almost immediately:

It turned out that the developer responsible for accelerating report generation for Tony modified the  calculateVat method to use naive rounding instead of the sophisitacated tax calculation algorithm. In the burden of other stuff present in  OrderService class, he didn’t notice that the method is also used by the  calculateTaxes method. Of course, we could argue that there should be proper tests and peer review and {put your preferred practice here} in place, but there is one obvious issue with this class — it does too much!

The Single Responsibility Principle

Every change done in a class’ logic can impact rest of the logic contained in that class. In our example, a change done to VAT calculation for the purposes of reporting impacted both the report generation and tax calculation processes. Likewise, a change for the purpose of improving the tax calculation could have affected the report generation. We clearly see that changes to two unrelated processes impact each other because they share a common class. That’s where the Single Responsibility Principle comes in:

A class should have one reason to change.

By the virtue of this principle, the two unrelated processes, as two unrelated reasons to change, should be put in separate classes.

Now, the two classes won’t probably share the VAT calculation, but if they do…

Who Owns the Tax Calculation?

That’s the other side of the Single Responsibility Principle. To make things clearer, I’ll modify the code a little bit:

class TaxCalculationService {
    TaxCalculation calculate() {
        ...
        Vat vat = calculateVat();
        ...
    }
}

class OrderReportGenerationService {
    TaxCalculationService taxCalculationService;

    OrderReport generate() {
        ...
        TaxCalculation taxes = taxCalculationService.calculate();
        ...
    }
}

Now, despite the fact that report generation and tax calculation are in separate classes, they still share some code. If Tony asks for some change to the report, Ben’s cents and euros might be in danger. Therefore, it’s crucial to remember:

The Single Responsibility Principle is [also] about people.

If there ate two parties/people with divergent requirements interested in a class’ functionality, there are two reasons to change that class, and therefore, SRP is not satisfied!

Now, to make both Ben and Tony happy, they deserve their separate classes for their separate requirements:

class AccountingTaxCalculationService {
    AccountingTaxCalculation calculate() {
        ...
        Vat vat = calculateVat();
        ...
    }
}

class ReportingTaxCalculationService {
    ReportingTaxCalculation calculate() {
        ...
        Vat vat = calculateVat();
        ...
    }
}

class OrderReportGenerationService {
    ReportingTaxCalculationService taxCalculationService;

    OrderReport generate() {
        ...
        ReportingTaxCalculation taxes = taxCalculationService.calculate();
        ...
    }
}

The Reality Check

Once you notice the possible negative consequences of mixed responsibilities, it might be tempting to go hunting and split your classes into smallest possible pieces so that no extra reason to change gets tangled anywhere. This other extreme is also bad, so before you do that, I’d like to add a bit of sanity ingredient to our mix. At the root of SRP, there are cohesion and coupling, the first of which is about keeping related stuff together. Therefore, same as you shouldn’t bash everything into a single class, you also shouldn’t split a cohesive class apart.

A really skewed example of applying SRP was presented in DZone’s Shades of the Single Responsibility Principle. The author suggests to split a CRUD service like this:

public interface PersonService {
    void deletePerson(String id);
    void updatePerson(String id, Person person);
    Person getPerson(String id);
    Person createPerson(Person person);
}

At first, it might seem reasonable because each of these operations is (probably) independent. We can’t make decisions about the interested people with so little information, so maybe it makes sense to make four services instead of one? (Hey, maybe let’s throw Ben and Tony in the example and make it eight?!)

The first thing that I’d question about this example is whether PersonService is actually a service or a repository. The methods it contains suggest persistence operations, not real business cases. Maybe the service layer is completely redundant here? Maybe a controller hitting a repository directly is the right approach here?

Yet in some projects, such CRUD orientation is reality, and each of the operations actually contains additional business logic. The questions we need to ask at this point are: how much? and how important is the independence of these operations for the stakeholders? What I mean by this is that if this “additional business logic” is a few, trivial lines of code, maybe there’s no risk involved and there’s a simplicity benefit to keeping those in one place.

I think that this last example nicely leads us to my final advice in this article: Use The Single Responsibility Principle wisely, as a problem prevention or problem-solving tool, not as an excuse to stop thinking.

Bitbucket is the Git solution for professional teams who code with a purpose, not just as a hobby. Get started today, it's free.

Topics:
single responsibility principle ,solid principles ,java

Published at DZone with permission of Grzegorz Ziemoński, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}