Is Inheritance really needed?
Asked Answered
L

22

37

I must confess I'm somewhat of an OOP skeptic. Bad pedagogical and laboral experiences with object orientation didn't help. So I converted into a fervent believer in Visual Basic (the classic one!).

Then one day I found out C++ had changed and now had the STL and templates. I really liked that! Made the language useful. Then another day MS decided to apply facial surgery to VB, and I really hated the end result for the gratuitous changes (using "end while" instead of "wend" will make me into a better developer? Why not drop "next" for "end for", too? Why force the getter alongside the setter? Etc.) plus so much Java features which I found useless (inheritance, for instance, and the concept of a hierarchical framework).

And now, several years afterwards, I find myself asking this philosophical question: Is inheritance really needed?

The gang-of-four say we should favor object composition over inheritance. And after thinking of it, I cannot find something you can do with inheritance you cannot do with object aggregation plus interfaces. So I'm wondering, why do we even have it in the first place?

Any ideas? I'd love to see an example of where inheritance would be definitely needed, or where using inheritance instead of composition+interfaces can lead to a simpler and easier to modify design. In former jobs I've found if you need to change the base class, you need to modify also almost all the derived classes for they depended on the behaviour of parent. And if you make the base class' methods virtual... then not much code sharing takes place :(

Else, when I finally create my own programming language (a long unfulfilled desire I've found most developers share), I'd see no point in adding inheritance to it...

Lowborn answered 10/11, 2008 at 16:59 Comment(8)
Don't create your own programming language. Please.Sonde
Let me augment that comment: Go ahead and create your own language. You'll probably learn a lot doing it. But PLEASE don't release it to the public! ;-)Cholesterol
Just to confront the previous negativity. Please go ahead and create your own language. And please release it to the public. At least you seem to be thinking about this stuff. Who cares if your ideas aren't fashionable. The worst thing that can happen is that you're the only user.Theodolite
Actually the worst thing is someone's time is wasted on "inventing" a language and complaining instead of doing something of real tangible value to others. While unlikely, the time lost can never be found again.Indihar
Good thing neither Wall, Matzu or van Rossum thought "inventing" a new prog. language some years ago was a waste of time. More recently, neither did Spolsky nor Graham, not withstanding the latter's big love for LISP it's not what he believes ideal.Lowborn
@Joe Pineda: To be specific, Paul Graham is extremely fond of Lisp, but not necessarily Common Lisp. Arc is very definitely a Lisp.Imbrication
@S.Lott: You are not exploring much, right?Khanate
I've found this article to be very helpful: among other things the author explains when inheritance is useful vs composition. Seems the author's answer would be "it's needed when it's useful and used right"Dorice
M
35

Really really short answer: No. Inheritance is not needed because only byte code is truly needed. But obviously, byte code or assemble is not a practically way to write your program. OOP is not the only paradigm for programming. But, I digress.

I went to college for computer science in the early 2000s when inheritance (is a), compositions (has a), and interfaces (does a) were taught on an equal footing. Because of this, I use very little inheritance because it is often suited better by composition. This was stressed because many of the professors had seen bad code (along with what you have described) because of abuse of inheritance.

Regardless of creating a language with or without inheritances, can you create a programming language which prevents bad habits and bad design decisions?

Musca answered 10/11, 2008 at 17:25 Comment(0)
T
22

I think asking for situations where inheritance is really needed is missing the point a bit. You can fake inheritance by using an interface and some composition. This doesnt mean inheritance is useless. You can do anything you did in VB6 in assembly code with some extra typing, that doesn't mean VB6 was useless.

I usually just start using an interface. Sometimes I notice I actually want to inherit behaviour. That usually means I need a base class. It's that simple.

Theodolite answered 10/11, 2008 at 17:32 Comment(0)
C
15

The GoF (and many others) recommend that you only favor composition over inheritance. If you have a class with a very large API, and you only want to add a very small number of methods to it, leaving the base implementation alone, I would find it inappropriate to use composition. You'd have to re-implement all of the public methods of the encapsulated class to just return their value. This is a waste of time (programmer and CPU) when you can just inherit all of this behavior, and spend your time concentrating on new methods.

So, to answer your question, no you don't absolutely need inheritance. There are, however, many situations where it's the right design choice.

Contaminate answered 10/11, 2008 at 17:34 Comment(5)
I believe most C++ compilers, at least, are able to optimize away a member function that does nothing but calling a parent-class member and returning verbatim the result. Agree it'd be indeed a programmers' time waste, though.Lowborn
Without inheritance though, there is no parent class though.Pronty
A language could provide syntactic sugar for automatically defining methods in terms of a delegate/helper object.Match
@Match Isn't that syntatic sugart the "extends" keyword? :) Most of the IDEs I work with have some type of code insertion where you can get all of the delegate methods (the public ones, at least)Sarge
export in Scala's dotty does thisTarsal
I
14

Inheritance defines an "Is-A" relationship.

class Point( object ):
    # some set of features: attributes, methods, etc.

class PointWithMass( Point ):
    # An additional feature: mass.

Above, I've used inheritance to formally declare that PointWithMass is a Point.

There are several ways to handle object P1 being a PointWithMass as well as Point. Here are two.

  1. Have a reference from PointWithMass object p1 to some Point object p1-friend. The p1-friend has the Point attributes. When p1 needs to engage in Point-like behavior, it needs to delegate the work to its friend.

  2. Rely on language inheritance to assure that all features of Point are also applicable to my PointWithMass object, p1. When p1 needs to engage in Point-like behavior, it already is a Point object and can just do what needs to be done.

I'd rather not manage the extra objects floating around to assure that all superclass features are part of a subclass object. I'd rather have inheritance to be sure that each subclass is an instance of it's own class, plus is an instance of all superclasses, too.

Edit.

For statically-typed languages, there's a bonus. When I rely on the language to handle this, a PointWithMass can be used anywhere a Point was expected.

For really obscure abuse of inheritance, read about C++'s strange "composition through private inheritance" quagmire. See Any sensible examples of creating inheritance without creating subtyping relations? for some further discussion on this. It conflates inheritance and composition; it doesn't seem to add clarity or precision to the resulting code; it only applies to C++.

Indihar answered 10/11, 2008 at 17:16 Comment(10)
How fast an answer! I understand your first point as indicating that, stricto senso, inheritance is not needed - it would merely simplify the task of having PointWithMass behave as a simple Point where needed. Did I misunderstood you?Lowborn
@Joe Pineda: If you think that egregious hack-around is "not needed", then you can take it that way. I think the option 1 hack is a terrible mistake, but some people use it to say "see, inheritance is optional" They're wrong in general, but right in a narrow technical way.Indihar
The answer is fast because it's a standard question. Many people struggle with inheritance and have all kinds of work-arounds because "is-a" somehow seems contrived or unnatural. A car is-a vehicle. Happens all the time. Answer is standard, too.Indihar
The thing with inheritance is that I can use a PointWithMass anywhere that was expecting a Point before without any changes to those methods.Hurst
"is-a" relationships are hard to get right. Once read an article about class "Set" inheriting from class "Bag" cause "a set is a bag that only accepts one of each kind". Then a minor change (allocating an object in heap rather than stack in constructor) in base class made a test app using Set crash.Lowborn
@Joe Pineda: Disagree. Bad implementation of anything can crash any client class that's a friend. Nothing to do with inheritance. Everything to do with bad design. Indeed, is-a is very easy to get right in spite of anecdotal evidence to the contrary.Indihar
I disagree. LOTS of people can't see why a Set is not a Bag, nor a Square is-a Rectangle, no matter how much common properties and behaviour they have. It's not a bad implementation issue - it's Liskov principle, which is difficult in practice, which makes inheritance hard.Lowborn
@Joe Pineda: Sorry. Not my experience. Never had problems with inheritance. None of my peers do. Indeed, don't have trouble with Liskov substitution in practice. So, I guess I haven't visited any obscure edge or corner cases in the previous decade of OO. Maybe next decade.Indihar
The only problem I see with inheritance is abuse and misuse of it. Sometimes it can be easier for the programmer to inherit rather than encapsulate, even though it would be far better to encapsulate. I dislike removing of features because people can't use them correctly (multiple inheritance?)Kinky
Note that all language features are victims of "abuse and misuse". Further, they can all be worked around, or replaced with something better or replaced with something worse.Indihar
L
14

The problem with inheritance is that it conflates the issue of sub-typing (asserting an is-a relationship) and code reuse (e.g., private inheritance is for reuse only).

So, no it's an overloaded word that we don't need. I'd prefer sub-typing (using the 'implements' keyword) and import (kinda like Ruby does it in class definitions)

Legit answered 10/11, 2008 at 18:9 Comment(2)
I couldn't have expressed it more clearly!!! A lot of the problems I've had with inheritance is because developers use it to get "free" reuse rather than assert an "is-a" relationship. Languages should keep the 2 issues separate!Lowborn
This is a great point. When object orientation was really "catching on" with the mainstream, code reuse was seen as the way forward in terms of reducing the cost of development. So the conflation ididak ids was seen as a feature, not a bug.Doleful
H
7

Inheritance lets me push off a whole bunch of bookkeeping onto the compiler because it gives me polymorphic behavior for object hierarchies that I would otherwise have to create and maintain myself. Regardless of how good a silver bullet OOP is, there will always be instances where you want to employ a certain type of behavior because it just makes sense to do. And ultimately, that's the point of OOP: it makes a certain class of problems much easier to solve.

Harding answered 10/11, 2008 at 17:30 Comment(0)
D
4

The downsides of composition is that it may disguise the relatedness of elements and it may be harder for others to understand. With,say, a 2D Point class and the desire to extend it to higher dimensions, you would presumably have to add (at least) Z getter/setter, modify getDistance(), and maybe add a getVolume() method. So you have the Objects 101 elements: related state and behavior.

A developer with a compositional mindset would presumably have defined a getDistance(x, y) -> double method and would now define a getDistance(x, y, z) -> double method. Or, thinking generally, they might define a getDistance(lambdaGeneratingACoordinateForEveryAxis()) -> double method. Then they would probably write createTwoDimensionalPoint() and createThreeDimensionalPoint() factory methods (or perhaps createNDimensionalPoint(n) ) that would stitch together the various state and behavior.

A developer with an OO mindset would use inheritance. Same amount of complexity in the implementation of domain characteristics, less complexity in terms of initializing the object (constructor takes care of it vs. a Factory method), but not as flexible in terms of what can be initialized.

Now think about it from a comprehensibility / readability standpoint. To understand the composition, one has a large number of functions that are composed programmatically inside another function. So there's little in terms of static code 'structure' (files and keywords and so forth) that makes the relatedness of Z and distance() jump out. In the OO world, you have a great big flashing red light telling you the hierarchy. Additionally, you have an essentially universal vocabulary to discuss structure, widely known graphical notations, a natural hierarchy (at least for single inheritance), etc.

Now, on the other hand, a well-named and constructed Factory method will often make explicit more of the sometimes-obscure relationships between state and behavior, since a compositional mindset facilitates functional code (that is, code that passes state via parameters, not via this ).

In a professional environment with experienced developers, the flexibility of composition generally trumps its more abstract nature. However, one should never discount the importance of comprehensibility, especially in teams that have varying degrees of experience and/or high levels of turnover.

Doleful answered 10/11, 2008 at 19:13 Comment(0)
T
4

Inheritance is an implementation decision. Interfaces almost always represent a better design, and should usually be used in an external API.

Why write a lot of boilerplate code forwarding method calls to a composed member object when the compiler will do it for you with inheritance?

This answer to another question summarises my thinking pretty well.

Thyself answered 10/11, 2008 at 22:29 Comment(0)
C
3

Does anyone else remember all of the OO-purists going ballistic over the COM implementation of "containment" instead of "inheritance?" It achieved essentially the same thing, but with a different kind of implementation. This reminds me of your question.

I strictly try to avoid religious wars in software development. ("vi" OR "emacs" ... when everybody knows its "vi"!) I think they are a sign of small minds. Comp Sci Professors can afford to sit around and debate these things. I'm working in the real world and could care less. All of this stuff are simply attempts at giving useful solutions to real problems. If they work, people will use them. The fact that OO languages and tools have been commercially available on a wide scale for going on 20 years is a pretty good bet that they are useful to a lot of people.

Cholesterol answered 10/11, 2008 at 17:39 Comment(2)
I have nothing against other people using the told they find more suitable for the work. I hate zealots promoting OOP as the one, only and true development paradigm. And being forced to use it as result. Plus, I've found OOP languages have more potential to be abused than structured ones...Lowborn
Reminds me Stroustrup's quote (take notice it's from C++'s creator): with C you can shoot your foot easily. With C++ it's slightly more difficult, though when you succeed, it takes off your whole leg. Probably it's inevitable: the more powerful the tool, the more destructive in beginner hands...Lowborn
G
3

There are a lot of features in a programming language that are not really needed. But they are there for a variety of reasons that all basically boil down to reusability and maintainability.

All a business cares about is producing (quality of course) cheaply and quickly.

As a developer you help do this is by becoming more efficient and productive. So you need to make sure the code you write is easily reusable and maintainable.

And, among other things, this is what inheritance gives you - the ability to reuse without reinventing the wheel, as well as the ability to easily maintain your base object without having to perform maintenance on all similar objects.

Greylag answered 10/11, 2008 at 17:47 Comment(2)
Good post. Be aware, though, that you can have reuse and maintainability with composition, too. In fact, a deep inheritance hierarchy often leads to "less" reusability, not more.Lowborn
@Joe, true.. there's a limit to anything's benefits. Too much of a good thing can result in bad.Greylag
S
2

There's lots of useful usages of inheritance, and probably just as many which are less useful. One of the useful ones is the stream class.

You have a method that should be able stream data. By using the stream base class as input to the method you ensure that your method can be used to write to many kinds of streams without change. To the file system, over the network, with compression, etc.

Spiritism answered 10/11, 2008 at 17:21 Comment(1)
But what keeps you from implementing an IStream interface instead? I think that was the focus of the original question. For example, a BufferedStream is a prime example of the utility of object composition over inheritance.Datha
S
2

Inheritance is a good thing when the subclass really is the same kind of object as the superclass. E.g. if you're implementing the Active Record pattern, you're attempting to map a class to a table in the database, and instances of the class to a row in the database. Consequently, it is highly likely that your Active Record classes will share a common interface and implementation of methods like: what is the primary key, whether the current instance is persisted, saving the current instance, validating the current instance, executing callbacks upon validation and/or saving, deleting the current instance, running a SQL query, returning the name of the table that the class maps to, etc.

It also seems from how you phrase your question that you're assuming that inheritance is single but not multiple. If we need multiple inheritance, then we have to use interfaces plus composition to pull off the job. To put a fine point about it, Java assumes that implementation inheritance is singular and interface inheritance can be multiple. One need not go this route. E.g. C++ and Ruby permit multiple inheritance for your implementation and your interface. That said, one should use multiple inheritance with caution (i.e. keep your abstract classes virtual and/or stateless).

That said, as you note, there are too many real-life class hierarchies where the subclasses inherit from the superclass out of convenience rather than bearing a true is-a relationship. So it's unsurprising that a change in the superclass will have side-effects on the subclasses.

Sinapism answered 10/11, 2008 at 17:30 Comment(3)
You can sort of do multiple inheritance in Ruby, but it's more a single inheritance language with mixins. C++ has true multiple inheritance.Harding
I had so many bad experiences with multiple inheritance in C++ that I actually liked the fact that Java and C# lack it. Especially the "diamond-inheritance" pattern (B & C inherit from A, D inherits from B&c) is VERY difficult to implement properly, with poor pay-offs in my opinion.Lowborn
Like I said, only the truly skilled should use multiple inheritance.Sinapism
M
2

No.

for me, OOP is mostly about encapsulation of state and behavior and polymorphism.

and that is. but if you want static type checking, you'll need some way to group different types, so the compiler can check while still allowing you to use new types in place of another, related type. creating a hierarchy of types lets you use the same concept (classes) for types and for groups of types, so it's the most widely used form.

but there are other ways, i think the most general would be duck typing, and closely related, prototype-based OOP (which isn't inheritance in fact, but it's usually called prototype-based inheritance).

Malefic answered 10/11, 2008 at 17:40 Comment(0)
S
2

Depends on your definition of "needed". No, there is nothing that is impossible to do without inheritance, although the alternative may require more verbose code, or a major rewrite of your application.

But there are definitely cases where inheritance is useful. As you say, composition plus interfaces together cover almost all cases, but what if I want to supply a default behavior? An interface can't do that. A base class can. Sometimes, what you want to do is really just override individual methods. Not reimplement the class from scratch (as with an interface), but just change one aspect of it. or you may not want all members of the class to be overridable. Perhaps you have only one or two member methods you want the user to override, and the rest, which calls these (and performs validation and other important tasks before and after the user-overridden methods) are specified once and for all in the base class, and can not be overridden.

Inheritance is often used as a crutch by people who are too obsessed with Java's narrow definition of (and obsession with) OOP though, and in most cases I agree, it's the wrong solution, as if the deeper your class hierarchy, the better your software.

Scrod answered 10/11, 2008 at 17:41 Comment(3)
For precisely these cases (default behaviour and overriding only a few methods) is where I believe composition is better. Inheritance, as in the "Go4" book, works here as a white box and so your subclasses depend on the implementation of superclass.Lowborn
Agree with you on this: too much Java and C++ developers use very deep inheritance hierarchies. This makes software very difficult to understand and maintain... Or probably that's the purpose, to ensure you'll have a job ;)Lowborn
only the really crap ones do that, but then I find those guys turn to easier languages like Java and C# and start criticising C++ for being "too hard" :-)Astrionics
L
2

Thanks to all for your answers. I maintain my position that, strictly speaking, inheritance isn't needed, though I believe I found a new appreciation for this feature.

Something else: In my job experience, I have found inheritance leads to simpler, clearer designs when it's brought in late in the project, after it's noticed a lot of the classes have much commonality and you create a base class. In projects where a grand-schema was created from the very beginning, with a lot of classes in an inheritance hierarchy, refactoring is usually painful and dificult.

Seeing some answers mentioning something similar makes me wonder if this might not be exactly how inheritance's supposed to be used: ex post facto. Reminds me of Stepanov's quote: "you don't start with axioms, you end up with axioms after you have a bunch of related proofs". He's a mathematician, so he ought to know something.

Lowborn answered 10/11, 2008 at 22:19 Comment(2)
well, "grand schema" designs by "architecture astronauts" always turn out to be a pile of pants. The problem is not the tool, but the workmen.Astrionics
Another way to look at it is that implementaion of inheritance is a good idea from the start, because likely, you'll find it useful after you create some classes. Not to say it's always the answer, but I find it much easier to add code than go back and change some base class's core functions...imhoBizerte
M
1

Not needed, but usefull.

Each language has got its own methods to write less code. OOP sometimes gets convoluted, but I think that is the responsability of the developers, the OOP platform is usefull and sharp when it is well used.

Myriam answered 10/11, 2008 at 17:34 Comment(0)
K
1

I agree with everyone else about the necessary/useful distinction.

The reason I like OOP is because it lets me write code that's cleaner and more logically organized. One of the biggest benefits comes from the ability to "factor-up" logic that's common to a number of classes. I could give you concrete examples where OOP has seriously reduced the complexity of my code, but that would be boring for you.

Suffice it to say, I heart OOP.

Knuckle answered 10/11, 2008 at 17:56 Comment(1)
Factor-up: In my own experience, when inheritance is used late in the project to group common functionality, the resulting system is easier to understand, factor and modify than when some "visionary" used inheritance from the very beginning. "You don't start with axioms, you end up with axioms".Lowborn
B
1

Absolutely needed? no, But think of lamps. You can create a new lamp from scratch each time you make one, or you can take properties from the original lamp and make all sorts of new styles of lamp that have the same properties as the original, each with their own style.

Or you can make a new lamp from scratch or tell people to look at it a certain way to see the light, or , or, or

Not required, but nice :)

Bizerte answered 10/11, 2008 at 17:59 Comment(0)
C
1

The biggest problem with interfaces is that they cannot be changed. Make an interface public, then change it (add a new method to it) and break million applications all around the world, because they have implemented your interface, but not the new method. The app may not even start, a VM may refuse to load it.

Use a base class (not abstract) other programmers can inherit from (and override methods as needed); then add a method to it. Every app using your class will still work, this method just won't be overridden by anyone, but since you provide a base implementation, this one will be used and it may work just fine for all subclasses of your class... it may also cause strange behavior because sometimes overriding it would have been necessary, okay, might be the case, but at least all those million apps in the world will still start up!

I rather have my Java application still running after updating the JDK from 1.6 to 1.7 with some minor bugs (that can be fixed over time) than not having it running it at all (forcing an immediate fix or it will be useless to people).

Cotterell answered 5/1, 2010 at 16:20 Comment(0)
B
1

//I found this QA very useful. Many have answered this right. But i wanted to add...

1: Ability to define abstract interface - E.g., for plugin developers. Of course, you can use function pointers, but this is better and simpler.

2: Inheritance helps model types very close to their actual relationships. Sometimes a lot of errors get caught at compile time, because you have the right type hierarchy. For instance, shape <-- triangle (lets say there is a lot of code to be reused). You might want to compose triangle with a shape object, but shape is an incomplete type. Inserting dummy implementations like double getArea() {return -1;} will do, but you are opening up room for error. That return -1 can get executed some day!

3: void func(B* b); ... func(new D()); Implicit type conversion gives a great notational convenience since Derived is Base. I remember having read Straustrup saying that he wanted to make classes first class citizens just like fundamental data types (hence overloading operators etc). Implicit conversion from Derived to Base, behaves just like an implicit conversion from a data type to broader compatible one (short to int).

Bite answered 30/3, 2013 at 17:34 Comment(0)
P
0

In the following, inheritance is used to present a particular property for all of several specific incarnations of the same type thing. In this case, the GeneralPresenation has a properties that are relevant to all "presentation" (the data passed to an MVC view). The Master Page is the only thing using it and expects a GeneralPresentation, though the specific views expect more info, tailored to their needs.

   public abstract class GeneralPresentation
    {
        public GeneralPresentation()
        {
            MenuPages = new List<Page>();
        }
        public IEnumerable<Page> MenuPages { get; set; }
        public string Title { get; set; }
    }

    public class IndexPresentation : GeneralPresentation
    {
        public IndexPresentation() { IndexPage = new Page(); }
        public Page IndexPage { get; set; }
    }

    public class InsertPresentation : GeneralPresentation
    {
        public InsertPresentation() { 
          InsertPage = new Page(); 
          ValidationInfo = new PageValidationInfo(); 
        }
        public PageValidationInfo ValidationInfo { get; set; }
        public Page InsertPage { get; set; }
    }
Photoelasticity answered 10/11, 2008 at 17:49 Comment(0)
P
0

Inheritance and Composition have their own pros and cons.

Refer to this related SE question on pros of inheritance and cons of composition.

Prefer composition over inheritance?

Have a look at the example in this documentation link:

The example shows different use cases of overriding by using inheritance as a mean to achieve polymorphism.

Pontone answered 7/11, 2016 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.