7

I've been aware of SOLID for many years now and I've always though about "OLID" was a good set of design principles to follow... problem is I've always found difficulties with the "S", I've always thought of it as a very ambiguous principle and believe me, I've read about this principle from many different places/books...

Today I've found this article and it sums up very well all my thoughts/concerns about SRP.

So I'd like to ask you, is it SRP really a principle as in the below pair of definitions?

  1. a fundamental truth or proposition that serves as the foundation for a system of belief or behaviour or for a chain of reasoning. "the basic principles of justice" synonyms: truth, proposition, concept, idea, theory, postulate; More
  2. a general scientific theorem or law that has numerous special applications across a wide field.

Why am I asking this? Well, it'd be great if you could apply SRP in a clear non-ambiguous way so you end up with classes or free functions and you can proudly logically say "this follows SRP" and you can prove that formally.

When I say "good" I mean something "logically speaking" correct. I like when the article says:

I really tried to understand the SRP, and to like it. I really tried. But I can't agree with it, and that's why I decided to publish this.

It's basically always been my concern, in this case I'll change from that sentence "that's why I decided to publish this" by "that's why I decided to asked here" so maybe some veteran expert coder can bring some pearls of wisdom.

19
  • 5
    I agree a lot with that article and it's followup. (I've been working on my own blog post, "SOLID Considered Harmful") SRP is miscomprehended by everyone - just check SO.:-) Open/Closed is just plain absurd on it's face, since nobody codes that way nor ever will. Even plugin systems, for which O/C kindof applies, modify their base class, and, in any case, plugin systems are far from the norm. (and, though I like them, they eventually have their own issues...) And LSP is, well, a lot of academic fighting over Squares and Rectangles with almost no relevance to the real world.
    – user949300
    Nov 13, 2017 at 0:35
  • 3
    I voted to close this as opinion based. That whole linked blog post reads as flame bait, and this isn’t a discussion site. And the linked article is not well argued. I think the author is missing some analytical tools. Nov 13, 2017 at 3:26
  • 3
    To me, SRP is 100% useless because it depends entirely on your definition of 'responsibility' or 'reason to change'. It's easier to just say 'don't have huge classes'.
    – 17 of 26
    Nov 13, 2017 at 13:35
  • 8
    I've got bad news for you. There aren't any software principles that are absolute. Nov 13, 2017 at 15:34
  • 3
    I see that the dictionary definitions you chose for the word "principle" are the ones Google gave you, but I like the first definition that Dictionary.com provides: an accepted or professed rule of action or conduct. Definition 6 is even better: an adopted rule or method for application in action. There's nothing fundamental about SRP; it is a guideline, nothing more. I happen to believe that it's a useful guideline, but it's a guideline nonetheless. Nov 13, 2017 at 17:55

4 Answers 4

20

I wouldn't use the term ambiguous, but without a doubt there is a significant element of situational judgement.

To answer your question up front, no, there is no formal, mathematically verifiable means of testing for compliance to it.

The reason why is because not all software programs and domains are the same. Joel touched on this in his essay Five Worlds. Huge, enterprisey business process management software requires different design approaches than software controlling a vending machine.

I will recognize the SRP is one of the most judgement-heavy concepts of the SOLID group, and there are tons of bad descriptions out there. Often times, written by authors solidly in the e.g. enterprise consultingware world, writing absolute statements completely unaware or ignoring aspects of other worlds. The "one reason to change" line people often write I find especially nonsensical and vague. I think the SOLID principles are best suited to the enterprise consultingware types of software, and the professional authors e.g. Uncle Bob often make a living selling their services to those kinds of companies. So, unless you too are in that world, you need to recognize what's appropriate for your situation.

A single responsibility may mean different things to different people in different situations. As an example, I recently needed a class to deal with a program's settings. That one class reads the config file, parses it, has the means to access the configuration values, and can write the configuration back to disk. All of that, in a single class, ConfigurationSettings. In my particular circumstance, despite doing those many different disparate things, I would claim my class does stick to a single responsibility: dealing with the configuration.

Now, some folks would say my design is defective, it does too much. I should inject in a config file parser object, have some abstract IConfig factory that instantiates the ConfigSettings class instead of new(), or a bunch of other things to further break up the responsibilities of that class.

Who's right? It depends on how the single responsibility fits in the greater picture. I've seen some authors tack on "at a given level of abstraction" onto the description of the single responsibility, which I think makes a lot of sense. And so in my situation, at my chosen level of abstraction, my ConfigurationSettings class does only a single thing, persist my program settings. The details of that config file format, where it's saved, etc, are things that just don't matter whatsoever in the greater picture, and no one cares where they go.

What kind of situation would there be where the abstract factory lovers would be right about my class? Often, they would be people in that mega-enterprisey world. My software has exactly 1 customer, and will never have more than that single customer. I can have an upgrade process that consists solely of "delete the old version, install the new one". If I introduce a change to the configuration settings that isn't backwards compatible to the old formats, I can just have them delete the old config and start fresh.

Not everyone can be so cavalier about their configuration settings. Some people may be writing software for hundreds or thousands of customers. They may have to manage and preserve different formats between different versions of their software. They may have to support a single configuration being shared by multiple computers, all reading from the same place. I don't have to do any of that. But for these other people, the file format, location, etc, does matter. In those situations, injecting a parser into my configuration class might make sense once you figure out what format the settings are in. It might make sense to have a factory do that figuring out. It might make sense to have a notification system for when settings get changed, instead of a bunch of static (global) variables like I'm using.

And if I was writing software for thousands of dental clinics to schedule appointments and do billing, my configuration class would without a doubt not be appropriate. But that's not what I'm doing, so it is.

Short of skynet-level AI, no static analysis tool is going to be able to look at a class, and understand what its intended level of abstraction is, and so thus can not make any meaningful determination of SRP compliance or not.

6
  • 1
    I think your example speaks more to YAGNI and “the design to meet the requirements” rather than the SRP. I’m sure you’ve heard the saying “do the simplest thing that could possibly work” as part of your development practice. That’s what I think of when I read your ConfigSetting example. The question of whether or not to have a separate class handle the File I/O depends on other things besides the SRP. For example there may be other parts of your code where files need to be opened, read, and written, and some people may not want to duplicate that code.... Nov 13, 2017 at 20:42
  • ... not only that but there may be a developer who knows file I/O very well and is tasked with improving its performance or taking into account differences between platforms where the code may need to run, or abstracting the storage from a file on a local disk to a file in a cloud service. So in that regard the SRP can tell us how to divide our classes when we have certain requirements to fill. On this view the SRP is clear and necessary. It’s useless to talk about unless you know what the requirements are and the problem to solve. Nov 13, 2017 at 20:46
  • The other way to think of it, is as an implementation detail. So the business requirements may not state that a separate I/O class is necessary but if the developer can build it separately without taking more time than he single class, then for example the class can now be tested more simply and the developer arguably made a higher quality program. You have to ask yourself what sort of developer you want to be. Nov 13, 2017 at 20:57
  • @RibaldEddie: "It’s useless to talk about unless you know what the requirements are and the problem to solve. " - that's precisely the issue. The problem is many people writing about the SRP don't take into account the wide range of complexity different requirements entail. Nov 13, 2017 at 22:08
  • Perhaps it can be stated that for the purpose of applying the SRP, a class’s responsibilities depend on what is needed in order to meet the requirements. Nov 13, 2017 at 23:46
5

The article in question seem to advocate "Big Ball of Mud"-architecture:

when one writes code, there are only real, present requirements. The future is pretty irrelevant

This is an extremely myopic view of development. After all, if we didn't care for the future, why even strive to write maintainable code? Why not always use the quickest and dirtiest hack to satisfy the present requirements?

In reality any developer worth their salt will design code with some anticipation of future changes.

The SRP just states this explicitly. We have to analyze what factors may cause requirements to change in the future, and attempt to structure code along these lines. You have to understand what different forces and interests affects the requirements.

In short, you have to understand the business and the organization.

You cannot "formally" and "logically" prove that some code follows the SRP - the world is far to messy and complex for this. Such a proof would have to involve not only the code but also the whole business and all outside forces which affect it.

1
  • I strongly agree with you that a good programmer should try to anticipate future changes. I disagree that the article advocates a "Big Ball of Mud". In large letters he writes that "classes should be: 1. small enough to lower coupling, but 2. large enough to maximize cohesion". He makes a reasonable argument that, often, "validation and persistence do belong together because they do normally change together", and admits that there may be cases to separate them.
    – user949300
    Nov 13, 2017 at 20:29
4

Bob Martin has clarified the SRP in a few talks as well as his new book. A quote:

Of all the SOLID principles, the Single Responsibility Principle (SRP) might be the least well understood. That's likely because it has a particularly inappropriate name. It is too easy for programmers to hear the name and then assume that it means that every module should do just one thing.

It doesn't mean that. What Mr Martin is trying to say is that any particular module should only be beholden to one change agent.

The identification of change agents is probably dependent on the size of the organization/project but once identified, it should be clear who can influence what modules.

For example, I write iOS apps. I have modules that are beholden to the UI designer, the UX designer and the server developer. If a change to the server can cause Module S to change, then there shouldn't be anything in that module that would have to change because the designer decided to change the size of a button. Nor should there be anything in that module that would have to change because the UX designer wanted to change the logical flow of the app.

In one of the other answers, an example of a "settings" class is used. As the class is described, it breaks the SRP because it would have to change if the method of saving information changed or if the logic of what needs to be saved changed. These are two different concerns which change for different reasons. Changing how things are saved would cause the class to be updated, and changing what needs to be saved would also cause it to be updated.

In Response To Comments: Basically the SRP is the principle you are following when you separate your view/presentation code from your model/logic code. It is the principle you are following when you separate the code that accesses the database from the code that decides what is saved in the database.

Further edit in response to comments: In the comments, the OP is making it clear that he is looking for an "unambiguous way" to determine who the change agents are, whether a particular module follows the SRP. There is, but it isn't universal. As a developer, you likely know who can and cannot make change requests, or maybe your code is based on several requirements documents, in which case those are the change agents. In my case, for example, I have use-case documents which define logic, then I have look-and-feel documents which define presentation, and I have server endpoint documents which define the server integration. Lastly, I also have my own technical needs which define things like storage strategy and areas in the code where I want to leave open the possibility of adding/removing external modules. So in my case, I have 4 change agents. In other companies, the storage strategy might be a company wide edict which would add a change agent. In other situations there might not be any server integration which would remove a change agent.

9
  • From all explanations about SRP i've read I'd agree Martin's to be one of the best attempts to clarify what this principle is all about... for instance, the way he explains it here is more or less kind of useful. But can you still say we're talking about a real principle? Is there any possible way to identify clearly who the "change agents" are clearly in a non-ambiguous way? Are these ones a static set or by contrary you can't be sure? Unless the organization defines strictly beforehand who the "change agents" exactly are the principle is useless
    – BPL
    Nov 14, 2017 at 3:43
  • Regarding to the "settings" class... your assuming there are those "change agents" you're talking about to prove he's violating SRP but from the perspective of the author of that class the "change agents" are pretty much different, that's why SRP is totally useless... you see? At the moment you're trying to prove another's person code violates this ambiguous principle you won't be able to agree easily (formally&logically) speaking about whether violates SRP unless both of you agree on the set of "change agents"/"responsabilities"
    – BPL
    Nov 14, 2017 at 3:51
  • Re: Settings, it depends. If you use a basic "read/write them all as JSON using standard library X" approach, there is no significant logic in what needs to be saved. And if your entire enterprise uses JSON (or XML, .ini, SQL, whatever) for everything, one could argue YAGNI that there is little chance of the method of saving changing. So, depending, the Settings have 0 - 2 plausible reasons to change.
    – user949300
    Nov 14, 2017 at 7:16
  • Regarding some Settings class... There is a very distinct difference between what's going to be saved and how it's going to be saved. "Save data using standard library X" tells us how, but it doesn't tell us exactly what needs to be saved. The logic of the app tells us what needs to be changed. In your example @user949300, the SRP is being followed (although the JSON requirement might be a leaky abstraction depending on circumstances.)
    – Daniel T.
    Nov 14, 2017 at 12:36
  • Is there any possible way to identify clearly who the 'change agents' are clearly in a non-ambiguous way? -- Yes. But there's a better name for that: Separation of Concerns. Nov 14, 2017 at 17:04
-1

It's an absolute statement. If someone suggests a way to split a class into two, where the new class doesn't need to access the original. Then the SRP says you should split it.

Also there is a limit to how far you can split any class. You only have so many lines of code. But even apart from that you only have so many parameters to play with once you are down to one method.

The assumption is that smaller classes are always better. They are definitely more flexible and reduce potential repeated code. I think you could academically say that that is better.

The only question is whether the business payoff is enough to justify the effort. Obviously you have a bit of boilerplate to write with every class you add. So unless you actually reduce code duplication in practice, its just more typing.

6
  • 1
    One of the benefits of smaller classes and particularly classes where dependencies are made separate, is on large development teams where multiple developers will be working on the code. When your have fewer, larger classes you will be more likely to run into merge conflicts. When you have many, smaller classes with very clear responsibilities, developers are more likely to make changes in different files and have fewer conflicts. Nov 13, 2017 at 17:57
  • that is also true
    – Ewan
    Nov 13, 2017 at 18:00
  • 1
    "If someone suggests a way to split a class into two, where the new class doesn't need to access the original. Then the SRP says you should split it." ... I don't believe this is true. At least to my understanding of it, the SRP actually says that in this situation you should make the split if there is some plausible change of requirements that will touch one class but not the other. In reality, there are many cases where you have no such reasons. In these cases, keep your class as it is. Either it will never change, or will be easier to change in its current form.
    – Jules
    Nov 13, 2017 at 20:21
  • thats the upshot of the principle + business reality. But in principle you can suppose any change of requirementskl with equal plausiblity
    – Ewan
    Nov 13, 2017 at 20:41
  • @Ewan No you can't. In principle you can't assume **** about what will change or not without thinking about your code. Any rule that pushes the idea that all changes are equally likely has no foot on logic.
    – T. Sar
    Nov 17, 2017 at 16:16

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