What is the use of single responsibility principle?
Asked Answered
L

4

6

I am trying to understand the Single Responsibility principle but I have tough time in grasping the concept. I am reading the book "Design Patterns and Best Practices in Java by Lucian-Paul Torje; Adrian Ianculescu; Kamalmeet Singh ."

In this book I am reading Single responsibility principle chapter , where they have a car class as shown below:

enter image description here

They said Car has both Car logic and database operations. In future if we want to change database then we need to change database logic and might need to change also car logic. And vice versa...

The solution would be to create two classes as shown below:

enter image description here

My question is even if we create two classes , let’s consider we are adding a new property called ‘price’ to the class CAR [Or changing the property ‘model’ to ‘carModel’ ] then don’t you think we also need to update CarDAO class like changing the SQL or so on.

So What is the use of SRP here?

Lynching answered 9/9, 2018 at 18:12 Comment(0)
A
9

Great question.

First, keep in mind that this is a simplistic example in the book. It's up to the reader to expand on this a little and imagine more complex scenarios. In all of these scenarios, further imagine that you are not the only developer on the team; instead, you are working in a large team, and communication between developers often take the form of negotiating class interfaces i.e. APIs, public methods, public attributes, database schemas. In addition, you often will have to worry about rollbacks, backwards compatibility, and synchronizing releases and deploys.

Suppose, for example, that you want to swap out the database, say, from MySQL to PostgreSQL. With SRP, you will reimplement CarDAO, change whatever dialect-specific SQL was used, and leave the Car logic intact. However, you may have to make a small change, possibly in configuration, to tell Car to use the new PostgreSQL DAO. A reasonable DI framework would make this simple.

Suppose, in another example, that you want to delegate CarDAO to another developer to integrate with memcached, so that reads, while eventually consistent, are fast. Again, this developer would not need to know anything about the business logic in Car. Instead, they only need to operate behind the CRUD methods of CarDAO, and possibly declare a few more methods in the CarDAO API with different consistency guarantees.

Suppose, in yet another example, your team hires a database engineer specializing in compliance law. In preparation for the upcoming IPO, the database engineer is tasked with keeping an audit log of all changes across all tables in the company's 35 databases. With SRP, our intrepid DBA would not have to worry about any of the business logic using any of our tables; instead, their mutation tracking magic can be deftly injected into DAOs all over, using decorators or other aspect programming techniques. (This could also be done of the other side of the SQL interface, by the way.)

Alright one last one - suppose now that a systems engineer is brought onto the team, and is tasked with sharding this data across multiple regions (data centers) in AWS. This engineer could take SRP even further and add a component whose only role is to tell us, for each ID, the home region of each entity. Each time we do a cross-region read, the new component bumps a counter; each week, an automated tool migrates data frequently read across regions into a new home region to reduce latency.

Now, let's take our imagination even further, and assume that business is booming - suddenly, you are working for a Fortune 500 company with multiple departments spanning multiple countries. Business Analysts from the Finance Department want to use your table to plot quarterly growth in auto sales in their post-IPO investor reports. Instead of giving them access to Car (because the logic used for reporting might be different from the logic used to prepare data for rendering on a web UI), you could, potentially, create a read-only interface for CarDAO with a short list of carefully curated public attributes that you now have to maintain across department boundaries. God forbid you have to rename one of these attributes: be prepared for a 3-month sunset plan and many many sad dashboards and late-night escalations. (And please don't give them direct access to the actual SQL table, because the implicit assumption will be that the entire table is the public interface.) Oops, my scars may be showing.

A corollary is that, if you need to change the business logic in Car (say, add a method that computes the lower sale price of each Tesla after an embarrassing recall), you wouldn't touch the CarDAO, since if car.brand == 'Tesla; price = price * 0.6 has nothing to do with data access.

Additional Reading: CQRS

Amick answered 9/9, 2018 at 18:29 Comment(8)
Thanks for your detailed explanation. So to make it simple , so I add property to Car class I need to make Changes in CarDAO also but the impact will be less compared to previous . Is my understanding correct?Lynching
Yes that is correct. A corollary is that, if you need to change the business logic, you wouldn't touch the DAO.Amick
Yeah got it , But what about model? If I do some changes to the model then I have to change DAO also right ?Lynching
If by model you meant Car.model, then you can rename columns in CarDAO without changing the interface of Car (and vice versa).Amick
'Model' I mean the car class itself, like renaming the property name or adding more property.Lynching
Oh I see. In that case, some changes have to be done on both sides, whereas with SRP there is a chance that some changes only have to be done on one side. For example, you could add a decade attribute to Car that is derived from Car.year, without changes to CarDAO.Amick
Cool. Got it !!Lynching
Could you answer again?) Quote from wiki " As an example, consider a module that compiles and prints a report. Imagine such a module can be changed for two reasons. First, the content of the report could change. Second, the format of the report could change. These two things change for very different causes; one substantive, and one cosmetic. The single-responsibility principle says that these two aspects of the problem are really two separate responsibilities"So if user role need some special column for report do i need to make double classes with almost same code only with new column?Puppet
E
3

For adding new property you need to change both classes only if that property should be saved to database. If it is a property used in business logic then you do not need to change DAO. Also if you change your database from one vendor to another or from SQL to NoSQL you will have to make changes only in DAO class. And if you need to change some business logic then you need to change only Car class.

Eighteenth answered 9/9, 2018 at 18:20 Comment(2)
So to put it simple , they SRP helps us to REDUCE the impact on the changes . Am I right ?Lynching
Yes, to reduce impact, to make parallel development by several developers faster and easier (avoid merge conflicts if one developer works on Car class and another on CarDAO class)Eighteenth
A
3

Single responsibility principle as stated by Robert C. Martin means that

A class should have only one reason to change.

Keeping this principle in mind will generally lead to smaller and highly cohesive classes, which in turn means that less people need to work on these classes simultaneously, and the code becomes more robust.

In your example, keeping data access and business logic (price calculation) logic separate means that you are less likely to break the other when making changes.

Avion answered 9/9, 2018 at 18:52 Comment(0)
U
0

I know this question is already accepted but I encourage you watch this youtube video which explained it with very easy example.

check below Amazon class, which has lots of responsibility, which is breaking the SRP, so we need to break this class and move similar functionality to same class

public class Amazon {

   // User related functionalities
    public void createUser(Map<String, Object> userInfo) {}

    public void changePassword(int userId, String password) {}

   // Order related functionalities    
    public void orderPlaced(Map<String, Object> products) {}

    public void cancelOrder(int orderId) {}

   // Notification related functionalities    
    public void sendNotification(String mode) {
        if(mode == "email") {}
    }
}
Unreason answered 19/5, 2023 at 12:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.