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.

Robert Harvey
  • 199,517
BPL
  • 455
  • 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 '17 at 00:35
  • Mmm, interesting you bring up to the table, thing is, I'm a big advocate of O/C principle in both dynamic/static languages and also a big fan of plugin architectures so I'll gladly like to read that article of yours to see the other side of the coin about it and give it some thought. Haha, I've laughed a lot about the "fighting over Squares and Rectangles" comment, +1 for that one :'D – BPL Nov 13 '17 at 01:41
  • @user949300: based on your profile, it looks like you haven't spent much time in the enterprisey software world. Open/Closed is useful in situations like when you have to do unholy things like simultaneously preserve buggy old behavior for some customers, have new fixed behavior for other customers, and have it all in a single version of the software. Consider yourself fortunate if you've never had to work in such a situation. – whatsisname Nov 13 '17 at 03:04
  • 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. – RibaldEddie Nov 13 '17 at 03:26
  • @whatsisname I worked on one large Enterprise system, but luckily we only had one customer, ourselves. So never encountered your mishmash of hassles. – user949300 Nov 13 '17 at 06:02
  • 1
    @RibaldEddie: the answer is "no", that is not opinionated. – Doc Brown Nov 13 '17 at 06:45
  • 1
    All principles are fundamentally ambiguous and opinionated in their application. That’s why they’re not axioms. – RibaldEddie Nov 13 '17 at 06:48
  • 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 '17 at 13:35
  • 1
    And then you would also have to discuss the concept of "size" in classes, as that's also subjective. Why not just stating "design software the way allows you to deouple teams and allow better coordination between themselves", where team is a set of 1 or more people. If SRP is getting on the way to allow better coordination/reviewing, what's its point? Guess one would have to understand the whole context SRP was stated back in the days (weren't some carnegie mellon guys who formulated it?). Anyway, i can't understand how SRP could meet the requirements to be considered as a principle at all. – BPL Nov 13 '17 at 13:52
  • 1
  • 8
    I've got bad news for you. There aren't any software principles that are absolute. – Robert Harvey Nov 13 '17 at 15:34
  • @RobertHarvey Totally agree with that statement, but that's just normal, humanity have been doing bridges for many years and the underlying processes involved have been refined and improved over and over. We've been making software in comparison for few years in comparison so it's still a relatively "new" type of engineering. Although you could claim it's not even engineering... but that would open another path to discuss, which is out of scope for this thread, which focuses about SRP. – BPL Nov 13 '17 at 17:39
  • 2
    What I am saying is that your question may be based on a false premise. We get people asking questions here all the time about The One True Way™. It doesn't exist. Debating whether or not SRP rises to the level of an actual principle seems especially specious. SRP is just an idea created by one guy to help people write better, more focused classes. – Robert Harvey Nov 13 '17 at 17:42
  • I'd say the question doesn't rely on a false premise of thinking about absolute truths in software engineering but instead, i'd say the main goal of the question would be whether is worth or not wasting time trying to apply a... principle? which doesn't add value to the final design. If you talk me about coupling or cohesion then yeah, those are useful concepts to architect a system but if you talk about responsabilities... you first need to clarify exactly (logically speaking without leaving room to discuss) what a responsability is, otherwise the principle is diffuse on its definition – BPL Nov 13 '17 at 17:50
  • 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. – Robert Harvey Nov 13 '17 at 17:55
  • SRP is essentially useless. Responsibility can mean many things, depending on the person you speak to, and lower level classes always provide multiple services to higher level classes. Hence, even if you say that a class has one responsibility, it is always possible to examine it from a different perspective, and see multiple responsibilities within. However, the intent of SRP is to avoid putting together unrelated code in one class, and that is still possible to achieve without a "principle". – Frank Hileman Nov 14 '17 at 02:11
  • Is asking if something can be "proven formally" a matter of opinion? – JeffO Nov 14 '17 at 14:46
  • 1
    The fact the question has been put on hold sums up pretty well what SRP is all about, a useless guideline where people won't be able to agree on it and producing superfluous discussions when reviewing code. It doesn't add any real value to come up with a good software architecture. Something which is a good principle shouldn't left any room of discussion about whether it's been applied correctly or not. In my book, following a real principle will produce a deterministic outcome where nobody will be able to claim "That doesn't follow that principle!" Because it either follows it or not... – BPL Nov 14 '17 at 17:50
  • @JeffO If it could be proven formally, it would not be a matter of opinion. Define "responsibility" formally, and you will discover any such definition is incorrect for many people. – Frank Hileman Nov 14 '17 at 18:36

4 Answers4

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.

whatsisname
  • 27,703
  • 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.... – RibaldEddie Nov 13 '17 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. – RibaldEddie Nov 13 '17 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. – RibaldEddie Nov 13 '17 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. – whatsisname Nov 13 '17 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. – RibaldEddie Nov 13 '17 at 23:46
  • interesting going through five worlds after all this time. it strikes me that now its four worlds as games have really become another form of shrink-wrapped software – jk. Nov 14 '17 at 12:36
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.

JacquesB
  • 59,334
  • 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 '17 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.

Daniel T.
  • 3,043
  • 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 '17 at 03: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 '17 at 03: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 '17 at 07: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 '17 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. – Robert Harvey Nov 14 '17 at 17:04
  • @RobertHarvey SoC is closely related to SRP, so... let me doubt about that absolute afirmation of yours, maybe our concepts of what's ambiguous is not the same here. For me, saying that SoC would be non-ambiguous is claiming that given a certain piece of code there will be one and only one set of "concerns", no matter the person who's analized/reviewed that piece of code. Said otherwise, there shouldn't have any possible discussion about the final set... so, do you still think the answer is yes? If it's so, could you please elaborate about it? As i'm really interested to hear it. – BPL Nov 14 '17 at 17:29
  • @RobertHarvey For instance, if some piece of is following Open/Close principle is a non-ambiguous principle, two different coders will be able to agree on whether a certain piece of code follows O/C or not straightaways. So the key here... and what i'm trying to really understand is... how to apply SRP in a way there won't be any room left to discuss whether this or that code really follows SRP, that way the precious time won't be wasted with irrelevant superfluous discussions about SRP has been applied correctly, that's the main concern I'm trying to clarify with my question tbh. – BPL Nov 14 '17 at 17:36
  • @BPL I added another paragraph to address your concerns regarding ambiguity. Also, your comment above implies that the Open/Closed principle is non-ambiguous. It isn't. No code can be closed against all possible changes and still open for any possible extension, but we still say a module follows open/closed if it is open/closed in the ways that are needed/expected in that particular system. Both of these principles are situation dependent. – Daniel T. Nov 14 '17 at 17:51
  • @BPL: The MV* paradigms like Model-View-Controller, Model-View-Presenter and Model-View-ViewModel are examples of the kinds of SoC I'm referring to. Where things properly go in these architectural arrangements is pretty well understood. – Robert Harvey Nov 14 '17 at 19:35
-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.

Ewan
  • 75,506
  • 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. – RibaldEddie Nov 13 '17 at 17:57
  • that is also true – Ewan Nov 13 '17 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 '17 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 '17 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 '17 at 16:16
  • @Tsar omg. 'in principle' means for a theoretical case. ie no, 'you cant assume shit' about anything that might happen, ergo yeah, all changes are equally likely – Ewan Nov 17 '17 at 16:40