2

I am using C++ , but as far as I understand most OO principles are cross language.

In most of the articles that I have read and liked about inheritance the advice are about :

  • not to use it for the purpose of code reuse.
  • inherit interface not implementations.
  • use "interfaces" (pure abstract class ) as base class.
  • prefer composition over inheritance ,and so on known articles about the abuse of inheritance.
  • prefer to work with interfaces for testability.

Examples:

Trying to improve my OO abilities and understanding I am reading this book: Head First Object-Oriented Analysis and Design and seen this usage in many other real life examples , where they put all common derived class attributes in the base classes.

In the book there is an example of guitar and mandolin classes , where base class have all the common members and the derived class have only the unique data.

enter link description here

I have seen these pattern in many other examples and in real life work.

The question is about the conflict between those two things, all the guidelines I stated, and contradicting examples from real life (or as examples of that book):

  • Maybe public interfaces has more strict "rules" from regular inner classes?

  • Or maybe those examples are bad, and I should continue to use inheritance only for interfaces (abstract classes with no members)?

I know that these are all recommendations and not state laws but I feel that I am missing something in my understanding .

Edit: The question is on the design stage, not the mess going on evolving code... If I try to design new classes and notice the same members between two classes with the same interfaces, should I put them in the base class?

Edit: added the example from the book that confused me, why inherit members?

Edit: this core guideline shows the problem I discussed and tries to solve it , but I think ,the solution is too complex.

"... For example, center has to be implemented by every class derived from Shape....How can we gain the benefit of stable hierarchies from implementation hierarchies and the benefit of implementation reuse from implementation inheritance"

11
  • am I confused between interfaces and regular classes inheritance ? so public interfaces has more strict "rules" from regular inner classes ? maybe it's just me, but I don't get what's the question or what are you struggling with. Would you mind rewording the question so it makes clear what's confusing you. If it's Avoid data members in the public interface. it only means that it's preferable to define members as protected or private and then define accessors and/or modifiers in the public section (interface), so no derived class can bypass the rules implemented in the abstract class
    – Laiv
    Aug 13, 2021 at 16:05
  • I edited the question, about the avoid members in interfaces , i think the public refer to the interface ,not to the visibility of the members. So the advice is to avoid members at all Aug 13, 2021 at 16:26
  • That doesnt make much sense. Even AbstractClasses has public and private interface. Maybe the the confusion comes from this arbitrary use if the word. By public interface Im sure Meyers is refering to visibillity.
    – Laiv
    Aug 13, 2021 at 16:33
  • 2
    @Laiv indeed, an abstract class has all the features of a class! OP uses the term abstract class in a more restrictive meaning (i.e. pure abstract class without data and static members), to make it equivalent to a Java interface (a quite common technique).
    – Christophe
    Aug 13, 2021 at 17:08
  • 1
    @Laiv i.stack.imgur.com/ZVKdZ.jpg Aug 13, 2021 at 18:27

4 Answers 4

1

There are more than one road to Rome. It is always a question of evolution and balance.

The quoted wording does not prohibit base classes with data members. It just recommends not to overuse inheritance and keep interface as simple as possible:

Abstract classes that are empty (have no non-static member data) are more likely to be stable than base classes with state- Core guidelines I.25

More likely does not mean that it's the only stable base.

In general, it's a good idea to prefer less inheritance.
- Uses and Abuses of Inheritance

This is more about avoiding inheritance when it's not needed. The rest of the quote is about prefering composition over inheritance. By the way, it's almost the same source, since Herb Sutter is co-author of the core guidelines.

Item 20: Avoid data members in the public interface
Scott Meyers

This is about proper interfaces and encapsulation. If a public interface defines accessible data members, any code could mess yours.

All these are very good advice. But none excludes the validity of having class hierarchies with a base class that is more than just a public interface. If it wouldn't be so, then OOP languages would have evolved differently and would have lost class inheritance to the exclusive use of interface implementation.

The key is just to get the right balance: not to overuse class inheritance, and to avoid deep hierarchies, because people are lost at which invoked methods is defined where.

Finally, don't forget that code evolves. Sometimes, you start with a specific class because a class is all what you need and there is no need of an interface at first sight (YAGNI). And later you might realize that after all, you need a more specialized class in some circumstances, and you just build on your base. And often it works quite well. This is another reason why you may find a lot of examples in legacy code.

A last thing: if you have a base class with data members, keep them as much as possible private, and let the derived classes use the public interface to access those members. This will greatly simplify maintenance and enforce robustness.

8
  • 1
    I dont really understand the balance idea. If i am guided to prefer abstract classes in one place , why i see usage of a lot of private members in a base class in a book , and as common practice in others code. Aug 13, 2021 at 17:57
  • The question is on the design stage, not the mess going on evolving code... If i try to design new classes and see similarities between two classes with same interfaces ,should i put them in base class Aug 13, 2021 at 18:01
  • 2
    @user3717741 Let me try another analogy: if we have a hammer in the hand every problem seems to look like a nail. The hammer is the abstract class and the nail is the interface. But we all know that a craftsperson needs more than a hammer to do the job. for every problem there is a suitable tool. Sometimes the tool is an abstract class, sometimes a normal class is more suitable. So its about suitability for the needs.
    – Christophe
    Aug 13, 2021 at 18:04
  • @user3717741 concerning your edit, my remark about evolving code is about why you find it so often in real life. The rest of my arguments are completely relevant at design stage. Btw, may I ask a silly question: do you only develop using a waterfall approach (i.e. first you make the perfect design, and only afterward you implement)? Or do you also develop in agile mode (i.e. the design of one iteration lead to a code that needs to evolve in the next)?
    – Christophe
    Aug 13, 2021 at 18:09
  • 1
    @user3717741 They don’t advise to avoid it. This is a misunderstanding. This is why I took some time to comment the quotes. So in short, having data members in a base class is perfectly fine: not every base class needs to be an empty interface. Java borrowed the interface concept from objective-c protocols, and funnily objective-c successor (in practice), Swift, introduced the possibility to enrich protocols with default implementations. So there is definitively a need.
    – Christophe
    Aug 13, 2021 at 18:37
1

Implementation inheritance is not bad per se. It is less flexible and this can be both a good thing and a bad thing. Implementation inheritance offers guidance and more elegant (less) code but if you make the wrong decisions you can paint yourself into a corner.

I try to design new classes and notice the same members between two classes with the same interfaces, should I put them in base class?

This is what they mean by "Do not use inheritance for the purpose of code reuse.". The only good reason for inheritance is an "is-a" relationship. If you get this wrong you will paint yourself in that corner and find you would have been better off with interfaces (which are not the same as abstract base classes by the way).

3
  • I have added the example that made me post this , on design stage they inherit from base class with attributes ,is it ok? i.stack.imgur.com/ZVKdZ.jpg Aug 13, 2021 at 18:26
  • @user3717741 External links to uploaded pictures will likely die at some point. Posts on this site should be self-contained. The picture does not tell which methods are abstract. Assuming the base class will not be instantiated, method matches(instrumentSpec) makes no sense because it does not match the signatures of the matches methods in the derived classes. If the derived classes would have the same argument type (instrumentSpec) it could work. Since both are string instruments I would expect the base class to be named StringInstrument and the getNumStrings member to be in the base class. Aug 13, 2021 at 18:59
  • An "interface" is basically a promise of what each class that "implements" that interface must do and provide. Oct 14, 2021 at 15:19
1

Many of your bullet point summaries about OOP are imprecise or misguided. I think this lies at the basis of the question at hand.

not to use it for the purpose of code reuse

Well, yes and no. Reusability is good, but there are many ways to make code reusable, and (novice) developers tend to overly rely on inheritance as their preferred approach, even in situations that do not warrant it.

Correct inheritance use is still done for code reuse purposes (through polymorphism), but not all code reuse should be done via inheritance.

inherit interface not implementations.

Semantical pedantry: you don't inherit an interface, you implement it. You only inherit classes. In that regard, your bullet points doesn't make sense.

This is similar to the above bullet point. Interfaces are a different way to tackle polymorphism and generally speaking (novice) developers need to be urged away from inheritance towards using interfaces, but that does not invalidate that there are use cases for inheritance. It just means that inheritance shouldn't be the default solution to everything.

use "interfaces" (pure abstract class ) as base class.

Interface != abstract class. While in some languages they have a functionally similar purpose, in other languages there are stark differences between them (e.g. you can only inherit from one class, or interfaces cannot have default implementations).

prefer composition over inheritance ,and so on known articles about the abuse of inheritance.

Same as before. Yes, (novice) developers need to be urged away from overusing inheritance, but that does not invalidate that there are use cases for inheritance. It just means that inheritance shouldn't be the default solution to everything.

prefer to work with interfaces for testability.

Yes, but specific to behavior-driven classes. DTOs don't follow this rule. They can, they just don't really need to as much as behavior-driven classes.


where base class have all the common members and the derived class have only the unique data

That is actually quite a good summation of what inheritance is. Generalize the shared commonalities and keep the specifics where they belong.

A lot of what you continue on with when you refer to a conflict with the included guitar/mandolin example seems to stem from a very strict and black-and-white reading of the above discussed bullet points. When you remove that strict interpretation, a lot of your constraints also relax and the conflict resolves.

While I can nit-pick at the implementation of the guitar/mandolin/instrument classes, your understanding of what the inherited instrument base class represents for the derived guitar and mandolin classes is correct.

0

If I try to design new classes and notice the same members between two classes with the same interfaces, should I put them in the base class?

No. not straight away. For example in CQRS, commands and events share very much the same structure. There could be commands bearing the same data as the resulting event. Or even between commands and events totally unrelated to each other. And sure both, commands and events will have the very same getters. Two or more classes holding nearly or the same members don't turn them conceptually into the same thing.

Why InstrumentSpec doesn't hold numStrings despite both instruments has strings. Because conceptually, not all the instruments have strings. Initially, you only need to operate with InstrumentSpec. Latter you find reasons to make InstrumentSpec behave in different ways, so you add GuitarSpecand MandolineSpec. Later you realise that your code is often casting to GuitarSpec because it has strings hence must be treated in more ways than just instrument specs. Later it's not just GuitarSpec you have more classes that also have strings and you are casting to all of them to treat'em like you did with GuitarSpec... Later, you realise that all these derived classes are being treated the same way because they all are a new concept StringedInstrumentSpec. A new abstraction that derives from InstrumentSpec and a new brand subhierarchy of classes that, despite the differences, they are all still instrument specs.

Through inheritance, you can express degrees of concreteness. Concreteness you don't want other components to be aware of. The details of derived classes are known by very few components. Components that

  • operate at the same level of abstraction
  • will be tightly coupled to the derived classes they work with. But that coupling is expected and, if in the right place, harmless.

This concreteness is also an expansion mechanism. Expansion means, provide the original concept with more features or allow it to do what it does, but in different ways.

why inherit members?

If members are intrinsic to the concept modelled by the abstract class, then the concept can not exist without these members. Well, that's not totally true. They can but at expense of the complexity (e.g the Shape example)

3
  • And a piano has strings, but you don’t count the strings, you count the keys on the keyboard.
    – gnasher729
    Sep 12, 2021 at 22:48
  • The piano is an acoustic, stringed musical instrument .... But yes, I get your point. It doesn't change the fact that if you need it to treat like a stringed spec, you can inherit from StringedSpec or create a whole new hierarchy under StringedSpec. But this is all guesswork, we don't know what's OP doing with these specs or what are they for.
    – Laiv
    Sep 14, 2021 at 16:19
  • In my breakdown I would have things like StringedInstrument, WindInstrument, PercussionInstrument, KeyboardInstrument. The Instrument root-class might not have any method (interface) other than play(). Oct 14, 2021 at 15:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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