3

Example 1:

Let me assume that I have a base class A. Class B extends Class A (Class B is a derived class).

Can I conclude, Class B doesn't obey single responsibility principle since it uses the concept of inheritance? I.e certain changes happen to Class A will require changes to Class B as well? Hence the responsibility for these changes is shared between Class A and Class B?

Example 2:

class Monitorable_stack extends Stack {
    private int high_water_mark = 0;
    private int current_size;
    public void push( Object article )
    { if( ++current_size > high_water_mark )
        high_water_mark = current_size; super.push(article);
    }
    public Object pop()
    {
        --current_size;
        return super.pop();
    }
    public int maximum_size_so_far()
    {
        return high_water_mark;
    }
}

What SOLID principles does this code obey and can some body help me in a better understanding the SOLID principles?

  • 1
    The first part of your question is extremely interesting. The second part of the question should be completed with a first guess of yours and why you think so. Otherwhise it would be too broad. – Christophe Mar 8 at 9:58
  • You probably mean "single responsibility" rather than "single relationship" in the second paragraph, right? – JacquesB Mar 8 at 10:30
  • Think of it this way. From class B's perspective, class A is an abstraction. So if it is a apple, it will stay an apple, even if apple changes. B is not responsible for keeping apple properties and apple behavior up to date. – Martin Maat Mar 8 at 11:35
  • IMHO the original wording of the question was very confusing, I had to read it three times, as well as some of the answers, to understand what you probably meant. I tried to fix this, please double check if I got your intentions right. – Doc Brown Mar 8 at 14:15
  • And you really should take the time and fix the second part of the question as Christoph has told you. – Doc Brown Mar 8 at 14:17
6

In Uncle Bob's article about Single Responsibility Principle the inventor of the principle explains :

SRP means that each software module should have one and only one reason to change.

But in his explanation, you'll quickly understand that the "reason" is something very fuzzy and more related to decision making than to objective technical elements:

This principle is about people.

So the whole wording of the principle is totally misleading. Fortunately, he provides an alternate wording:

Gather together the things that change for the same reasons. Separate those things that change for different reasons.

And here everything becomes clear: gather in A everything related to the general A concept (e.g. a Vehicle, a Stack), and in B everything related to the specialisation of A (e.g. a Car, a MonitoredStack). Do not mix the responsibilities (e.g. do not change general Stack behavior unrelated to monitoring in MonitoredStack, do not introduce monitoring issues in the Stack base class ).

So inheritance, if done properly, does not go against SRP. It goes against SRP only if changes in B require changes in A.

Finally, if inheritance would go against SRP, SOLID would not exist, because inheritance is the core of O (Open/Close) and it would then contradict the S.

| improve this answer | |
  • 1
    Your second to last paragraph is the clearest explanation I've ever heard for SRP. – Robert Harvey Mar 8 at 15:04
  • 1
    Yes, it's excellent. Plus, also: "SRP is a guideline." A "rule of thumb." – Mike Robinson Mar 10 at 17:10
1

Wait trying to apply the single responsibility principle for a few years. As a learner you will inevitably get it completely wrong.

Whenever you want to do something quite reasonable like using inheritance and you think you shouldn’t because of SRP, it is most likely that you understood SRP wrong.

In this case, changing the implementation of Stack doesn’t affect the subclass. Changing the interface of Stack affects everyone so your subclass is in the same situation as any other code. Nothing to do with SRP.

| improve this answer | |
1

Inheritance can indeed be problematic because it is easy to create unnecessarily tight couplings. They are not always bad, but you should always consider if the same can be achieved with composition.

For example, in your example the responsibilities actually seem pretty well separated. The implementation of Monitorable_stack is not coupled to the internals of stack. But this also means you don't need to use inheritance in the first place. The Monitorable_stack could be an adapter which wraps a stack and present the same interface, but does not inherit from stack. This avoids the risk of coupling to the internals, but at the price of more boilerplate code: You have to delegate all methods in the public interface to the underlying stack, while inheritance allow you to only override the ones you need to change.

A worse form of coupling would be if stack had a non-public current_size which monitorable_stack observed instead of keeping its own. If then the implementation of stack changed to not use this counter anymore, monitorable_stack would break (and possibly other subclasses). SOLID has the Open/Closed principle to avoid such problems, but it is much better to just avoid the coupling in the first place.

The single responsibility principle is confusingly named and often misunderstood, so I prefer to talk about coupling and separation of concerns.

| improve this answer | |
0

Can I conclude, Class B doesn't obey single responsibility principle since it uses the concept of inheritance?

No. SRP focuses on each class having a single responsibility, e.g. that FooRepository knows how to retrieve Foo objects, but it doesn't e.g. know the specific implementation details of how to log messages when it can't find the object it's looking for. That secondary responsibility should actually be the (sole) responsibility of a different class, e.g. Logger. Every class knows how to do one thing, and that's what SRP prescribes. Classes have one responsibility, and then some dependencies on other classes (each with one responsibility) if they need to do more than just one thing.

SRP doesn't focus on the inverted, i.e. two classes having a similar (but in some way different) responsibility. What you're thinking of is DRY (Don't Repeat Yourself), which focuses on needless code repetition.
Although these two similar-but-different classes would usually violate DRY in some way, they don't violate SRP. And that's regardless of whether these two classes have a common ancestor or not.

I.e certain changes happen to Class A will require changes to Class B as well?

That's just not how inheritance is supposed to work. In a clean codebase, the only changes to A that necessitate changes to B are changes made to the contract of A. The consequences of such changes (i.e. needing to change B) is unavoidable; regardless of SRP.

If non-contract changes in A cause you to have to make changes in B, then you're not implementing inheritance properly (or cleanly). Even in an inherited hierarchy, each class has its own responsibility. They're closely related on a functional level, but they don't actually overlap. The entire point of inheritance is to divide the (otherwise combined) class' responsibilities into two separate chunks.

Hence the responsibility for these changes is shared between Class A and Class B?

"Responsibility for changes" isn't a concept, certainly not in regards to SRP.

Changes to a public contract will always lead to (possibly) needing to change the usage of this contract by your consumers. That's just a logical and unavoidable fact, by definition of what a contract exactly is.

What SOLID principles does this code obey and can some body help me in a better understanding the SOLID principles?

This isn't really answerable based on your snippet.

  • (S) Responsibilities are not clearly delineated, context isn't clear; cannot discern what is a single responsibility and what is a conflation of responsibilities.
  • (O) Can't be decided based on only seeing a derived class and not its base. The intention of the inheritance seems to adhere to OCP but that's just a wild inference.
  • (L) No use of polymorphism in the shown code. Unsure what the consumer's expectations of the base class are and how it's intended to be consumed.
  • (I) No interfaces used in the shown code
  • (D) No dependencies used in the shown code
| improve this answer | |

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.