This is more of an OOD problem and I have no specific code to post here. Can the same class violate the SRP in one context and be SRP compliant in other without changing a single line of code?
In other words, can there be a situation that within the frameworks of the former requirements the class is SRP compliant and then, when requirements change, it is no longer compliant.
1
Good question.
First, the easy one: Different contexts, as in the single responsibility of a class, or of a method, or of a component, or microservice, or what-have-you. In those terms, the SRP is clearly context dependent, though you are talking about different units in each context.
Your question of fixing the unit whilst changing the context is more interesting of course.
However, I fail to see a way in which the SRP is violated by that. The responsibility itself will certainly change though. For example, a class could be a DTO-POJO for one framework and then you switch frameworks and it has to be a JPA entity or something. It still has a single responsibility though and you would have changed the code.
Having a SRP compliance difference without changing the code, however, would require your responsibilty to be directly dependent on the requirement. However, this still should not lead to a compliance problem on change. When you think about it, the old requirement is tightly coupled to the responsibility of your unit, then changing the requirement means that your code has to be changed as well or it will no longer satisfy the new requirement. Given that your code is plain and simply wrong at that time, discussing the merit of its SRP compliance becomes sort of void.
In short: making changes to frameworks/requirements is highly likely to void the necessity of your responsiblity, but the unchanged unit still only has its single responsibility. However, that responsibility may no longer be deemed correct or necessary.
The Schrödinger’s class (pun intended).
Let’s think about it… it’s a mind-bending brain excercise. If the change of context implies a change in the level of abstraction…
If in context A we have a higher level of abstracion, meaning that the class models the reality in a coarser way and then we change to context B (because requirements changed) in which the level of abstraction is more fine-grained, then there could be a situation in which we need more classes, because new responsabilities need to be addressed. We would be violating SRP if we code the new responsabilities into the class, otherwise it’s impossible such violation because no code has been added.
So basically if such a situation seems to happen it only means the class doesn’t fulfil the new requirements, or the class was already doing too much to begin with. For the class to have more responsabilities than before, there must be addition of code. So I think it’s not possible.
Colophon
One of the main purposes of OOP is to make it easier code reuse. That means that a class can be part of a library used in more than one project or context, and I’m not even talking about third party libraries (that would be the perfect excuse), it could be a library whose source code you control but that is used in different projects. So, to make the whole thing more similar to the paradox of the Schrödinger’s cat, if SRP were context dependant then the class would be simultaneosly violating and not violating SRP.
1
If a class violates the SRP in context A, the usual fix is to split it up into smaller units. And if you can do that (without throwing away any existing functionality), you can reuse that units also in a different context B. But if the original unsplitted class would not have been violating the SRP in context B already, the usage of the splitted units in context B would either not be possible, or at least not make much sense. However, if those units made sense in context A, I don’t see how they cannot make sense in context B. Thus my opinion is simply no.
Of course, there might be edge cases where the placement of a certain function in a class is a violation of the SRP, but this maybe tolerable in context A as a pragmatic solution, because there is no other good place for that function within A. However, in context B there might be a much better place for the function in a different class, so one might say in B the violation of the SRP is more severe. But I don’t think this affects the general notion of the SRP beeing a property which is mostly context independent.
Yes, oh yes.
Responsibility isn’t what you can do. It’s what you are relied on to do.
I can answer questions and I can make awesome french toast. Those could all fall under the responsibility of being an entertaining host. But since no one here is counting on me making french toast it’s not one of my responsibilities here.
A more code based example could be a full name class. This value object holds and exposes a first, middle, and last name. All are needed to assemble the full name. This is all one responsibility so long as there is code relying on these fields. Drop this class into code that only works with last names and suddenly it’s doing to much.
Similarly if that full name class was dropped into a code base where one package used last names and another used first names then again this full name class has too many responsibilities.
A single responsibility means having only one reason to change. That doesn’t mean one object can’t satisfy the needs of many systems. It can. It means if those systems don’t agree on their needs they need to use different objects. The object should be able to ignore whose needs it’s fulfilling. It shouldn’t have multiple faces it turns to whoever needs them.
8