4

Can you in "plain English" explain why we need Single Responsibility Principle?

Especially that it moves the what I call "bloat" into other places. See below for more info. By bloat I mean in my specific case, I have multiple responsiblities in a Controller that exhibit themselves as functions/methods. By applying SRP in the way that I understand, breaks up the Controller into several Controllers, each with one method. The "bloat" is therefore moved from "number of methods" into "number of files".

The article in the wiki link above explains what SRP is, but it does not seem to tell me "why" we use it. I'd like a not-so-technical (or maybe even a technical) reason/explanation/sense as to why we need this principle.

Because in my current experience, implementing SRP, leads to smaller, more narrowly-aligned code, but it creates more files.

Example

I have a controller file that can have many various actions/methods inside of it. To keep all those actions in the controller, I have to bloat the number of dependencies I have to pass to the controller, because dependencies must cover all possible actions, even if any one particular action does not need all of the dependencies.

so I can break up the controller into 2 or more pieces to where I have one action per controller. This satisfies the SRP, but it bloats the number of files I have to create.

Example

/*
 * Repository initialized with $quoteId
 */
class Repository
{
    private $quoteId;

    function __construct($quoteId)
    {
        $this->quoteId = $quoteId;
    }        
}

/*
 * Controller initialized with $repository
 */   
class Controller
{

    private $repository;

    function __construct($repository)
    {
        $this->repository = $repository;
    }        

    function addForm()
    {
         //repository is initialized elsewhere with $quoteId
         $this->repository->getFormDataFromQuote();
    };

    function viewForm()
    {
         $id = int_val($_GET['id']);

         //does *not* use $quoteID
         $this->repository->getViewDataFromId($id);
    };
}

SRP

To abide SRP we can break up Controller into two, one for addForm, one for viewForm. We then can break up the Repository into two as well, one for each method in Controller. Thus, we started with 2 files, we will end up with 4 files.

Drawbacks

I interpret SRP here as "Break up the controller" (and, I presume any further supporting files as well, such as Repository here) into two in this case. Thus in the above example, there will be ControllerAddForm and ControllerViewForm, and if I am using a repository for those methods, which I am, I have to create RepositoryViewForm, and RepositoryAddForm to satisfy SRP as well, because different repository methods are required for the different actions of the Controller. Thus, I get 2x file bloat. Why again is SRP recommended despite moving the bloat into the number of files instead of into the number of methods per file.

  • 3
    It sounds like you are confusing functions performed with responsibilities. Something can have a single responsibility, such as managing a user-entered form, but it will perform many actions. Persisting the form to a backing store would constitute another responsibility that is not (directly) part of your Controller, since the encoding and saving/restoring responsibilities are different. – BobDalgleish Nov 7 '17 at 21:59
  • 1
    "Responsibility" is a vague term but a single responsibility in higher level classes can mean multiple responsibilities from a lower level perspective. – Frank Hileman Nov 7 '17 at 22:08
  • 3
    SRP is recommended because people usually combine it with common sense. In a hotel, you have a chef whose single responsibility it is to cook all the foods required. You don't have one chef responsible for fried eggs, one responsible for boiled eggs, one responsible for scrambled eggs, and a few hundred others. – gnasher729 Nov 7 '17 at 22:08
  • 1
    Or responsible just for the eggs, for that matter. – Robert Harvey Nov 7 '17 at 22:10
  • 2
    @gnasher729: there is no such thing as common sense. – whatsisname Nov 7 '17 at 22:23
12

You need SRP for the same reasons you don't let your Sous Chef fix your car. They have different responsibilities.

Each person specializes in a different area of expertise. If it helps, think of your classes as employees. Give each one a job title; that's usually the name of each class. Work out the "area of expertise" of that class, and then write code that fulfills that area of expertise.

  • 3
    I like this way of thinking of it. Mostly because it means when I'm thinking about the CarRepair class I won't be thinking about how much salt to add. – candied_orange Nov 7 '17 at 23:24
  • 2
    @CandiedOrange Table salt is naturally abrasive and absorbs some grease, making it a surprisingly reasonable choice when doing car repair. – Joel Harmon Nov 8 '17 at 23:31
  • @CandiedOrange: you must not know many <insert brand of choice> mechanics. They're full of salt! – Bryan Boettcher Nov 9 '17 at 15:19
  • The metaphor is dangerous a bit. You might let your Sous Chef fix your car if he fixed cars in his spare time and was good at it. The reality is that specialization (managing responsibilities) takes cognitive effort both with people and with software classes. If someone came up with a company that fixed cars and served gourmet meals to customers while they waited, this kind of design could work. I think there's more to SRP than this... – Fuhrmanator Nov 10 '17 at 19:45
  • @Fuhrmanator: In software, generally your Sous Chef doesn't fix your car in its spare time. That's kind of the whole point here. If you genuinely needed a Sous Chef that Fixes Cars in His Spare Time, you would undoubtedly create a class that inherits from SousChef, and you'd still be squarely in SRP territory. So no, there's no magic here, and yes, it is just a metaphor. – Robert Harvey Nov 10 '17 at 20:16
12

SRP is one of the most misunderstood software engineering principles. There is not even a precise definition of what a "responsibility" is, making this more difficult to understand.

Roughly speaking, a "responsibility" is not a single atomic action, but rather, a set of actions that are closely related. Adding a form, viewing a form, submitting a form, etc. are all the same responsibility of "managing form X."

To abide SRP we can break up Controller into two, one for addForm, one for viewForm.

No. To abide by SRP you have separate controllers for managing quotes and managing customers. They may delegate to models that manage persistence for each data type (quotes and customers).

In general, refactoring large units of code into smaller units of code is a good idea up until the point that there are too many small units of code to reason about. Personally, I think that keeping all of my "manage quote form" code in one class is a great idea: I know exactly where to look for controller code for quotes. I know all of the code is there for that purpose, but no code for managing customers.

Also note that SRP as it applies to a web-based quote management system will be different than with the Linux kernel. Take a step back and look at the overall design, and try to find the natural divisions in what the code is doing. Then ask one of your peers.

  • 3
    Frankly, most of the SOLID principles are so vague as to be useless unless you already understand the design principles that motivate them. – Derek Elkins Nov 8 '17 at 9:12
  • Thanks. I still don't understand it, so I'm trying to get more clarity on SO (stackoverflow.com/questions/47169129/…) – Dennis Nov 8 '17 at 15:17
  • @Dennis your other question was closed as too broad, which I would expect. I am not sure stack exchange is the right place for such an open-ended question. Perhaps /r/learnprogramming might be able to help? I searched there and found this post which at first glance seems to contain some good advice. – user22815 Nov 9 '17 at 1:01
  • 4
    SRP seems to be extreme in that regard. Refactoring into smaller units because one unit does too much is generally fine. Refactoring into smaller units because of SRP is usually motivated by incorrect understanding of SRP. – gnasher729 Nov 10 '17 at 21:56
3

SRP is dangerous (as are most design principles) if you follow it as dogma. My perspective is to respect SOLID but beware of its strong cult(ure), especially when someone's shedding more heat than light with citing those principles.

When I read your question, I thought of SRP as being in opposition to YAGNI.

Using your controller example, it makes little sense to separate your controller out into distinct classes if the requirements never change, you won't be maintaining the project after some "Shark tank" demo, and/or you'll never need to reuse part of your controller logic in another project. They're all big ifs maybe.

Robert Martin gives a detailed example using code in a bowling project. The design leads to a class Game that has two responsibilities: keeping track of frames, and calculating the score.

enter image description here

In the pair-programming design experiment in his book, one of the developers ends up suggesting to refactor these separate responsibilities into distinct classes, Game and Scorer, citing SRP as the motivation. A sarcastic comment is made that Linux programmers like to do everything in one single unreadable function...

It's a good example of the Ying and Yang of the SRP. Having separate modules for the Game and Scoring responsibilities has an advantage of allowing the two to evolve more independently, perhaps even allowing a new programmer to find where in the code that tricky bug in scoring might be (there are fewer lines in Scorer than if all the code were combined in one module), etc. These classes could theoretically be reused in other applications that need their services (Jarts anyone?).

But there's a down side: anticipating changes that won't ever occur is a waste of effort. What changes could possibly come to the rules of the game of bowling!? Theoretically they exist (hey, there could be an executive order from 45?) and if they happened, having separate modules would surely be better then, right?

Alas, YAGNI says don't waste your energy on changes until they come. It's over-engineering.

Finally, SRP (and responsibility-driven design in general) is a heuristic (meaning it seems to have worked in the past, but there's no guarantee that by applying it you will benefit). Nobody's done enough research to tell you when it pays off. Design problems are hard, and trying to use a "principle" dogmatically will lead to disappointment.

I found the Principles Wiki really interesting (albeit incomplete) because it attempts to map out the relationships between these various design principles. For SRP, there are many related principles, e.g., separation of concerns, Curly's law, etc. Reading those will also help you understand the trade-offs.

  • 1
    I thought of SRP as being in opposition to YAGNI. Also in opposition to KISS and non-clever programming. By not subdividing the code into cleverly named subproblems the code will be more readable from beginning to finish and won't cleverly hide away what's happening. – morbidhawk Nov 21 '17 at 22:22
1

The Single Responsibility Principle (SRP)

Classes should not have more than one focus of responsibility. Business context will also drive the level of segregation of responsibility in a class.

Note:

  • Classes can reasonably be involved in different interactions, it is the focus that is the issue.
  • This principle is almost identical to the cohesion principle

SRP Analysis: For example, look at each method, x, in the class, say Car, and ask, "does Car have primary responsibility for x-ing?" If the answer is no, the method may not belong there. Ask with regard to the object, not the action.

  • 3
    I even think that SRP principle is on top of the concept of cohesion. All the SOLID can be derived from these two: strong cohesion and low coupling. – Zapadlo Nov 8 '17 at 7:33
  • Martin gives yet an even subtler example of a Rectangle.draw() and Rectangle.area(). The class seems to be OK until you consider how the GUIApp might require a change that is incompatible with the ComputationalGeometryApp (or vice-versa). The SRP is applied to create a new class GeoRectangle taking the area() method from the original Rectangle. Although the example makes sense, it doesn't convince me what kind of change would break the original design. It's what I'd call more "heat" than "light" when the example's not concrete enough. – Fuhrmanator Nov 10 '17 at 22:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.