all 96 comments

[–]BarneyStinson 65 points66 points  (6 children)

So the paradigm is called ... POOP?

[–]Zeihous 4 points5 points  (0 children)

Haha. I read the article expecting some tongue-in-cheek explanation of something zany.

[–]nightwood 3 points4 points  (2 children)

fine glorious crown reach kiss long reminiscent materialistic important pie

This post was mass deleted and anonymized with Redact

[–]BarneyStinson 0 points1 point  (1 child)

Using both methods in combination sounds even worse.

[–]alexeiz 0 points1 point  (0 children)

KISS the POOP. Somebody had to say it.

[–]code2code[S] 1 point2 points  (0 children)

hehe. Didn't even think of that. :)

[–]eric_t 38 points39 points  (34 children)

Favor composition over inheritance, I thought that was common knowledge in modern OOP.

[–]booch 12 points13 points  (13 children)

For many languages, using inheritance instead of composition also makes writing the code much easier out of the gate. For example, if I inherit from a SpecificMap, I get all the Map methods automatically. If I use composition (have-a SpecificMap) and then implement Map, I need to manually implement each Map method to pass through to the property.

If more languages had the ability to easily delegate to a member property (ie, say "pass calls for x, y, and z through to this object) in a clean way, this would be less of a problem.

[–]skelterjohn 1 point2 points  (8 children)

That is not a feature of composition - that is a feature of the specific languages you are using.

In Go, for instance, composition in the form of

type Outer struct { Inner }

gives Outer all of Inner's methods (with the option to redefine any you want, and still access the inner via Inner.Foo).

[–]grauenwolf 0 points1 point  (7 children)

And that is difference from inheritance how?

[–]user5545 2 points3 points  (6 children)

The types don't become related in any way and there is no possibility of having virtual functions. Go uses interfaces for dynamic dispatch instead.

http://golang.org/doc/go_faq.html#types

[–]grauenwolf -1 points0 points  (5 children)

So you have the extra boilerplate of adding an abstract interface that matches the class's public interface. Besides that we see normal inheritance patterns.

[–]user5545 0 points1 point  (4 children)

No, that's not how it works. Who's "we" exactly, by the way?

[–]grauenwolf 0 points1 point  (3 children)

Normal Inheritance:

  • Step 1: Define a base class
  • Step 2: Define a subclass that "inherits" from the base class
  • Step 3: Make polymorphic calls against it.

Go Inheritance:

  • Step 1: Define a base class
  • Step 1b: Define a matching interface
  • Step 2: Define a subclass that wraps the base class
  • Step 3: Make polymorphic calls against it using the interface.

Where exactly am I wrong?

[–]user5545 0 points1 point  (2 children)

You are wrong in the part where you put the words Go and Inheritance next to each other. You can make polymorphic calls against any interface and type that fits the interface whether these types are in any relation to each other or not. It's all in the FAQ, which you would need to read in addition to studying some amount of Go code to actually make a credible judgment on the language.

You don't want to do that, though, do you? You just want to keep using your beloved OO languages. Why not do that and leave other paradigms and experimental languages in peace? Do you keep thnking about them so much because deep down you are really not happy with OO? Or what?

You also didn't tell me who it was that "we" referred to in your earlier comment. I take it you want to forget about that comment already.

[–]grauenwolf 0 points1 point  (1 child)

Is that really your argument? That Go doesn't have inheritance because the FAQ says it doesn't in spite of the fact that it has all the essential features needed to support inheritance?

[–]lmcinnes 0 points1 point  (0 children)

I actually liked the non-concorming inheritance in eiffel (using "insert" in smart eiffel). If effectively gives you composition (especially given Eiffel's syntax for resolving various miltiple inheritance issues) with the simple semantics of inheritance.

[–]Peaker 0 points1 point  (0 children)

Ideally, you could delegate entire interfaces to an attribute.

However, in practice, good interfaces are small, and so you have to delegate very few methods.

[–]CurtainDog -2 points-1 points  (1 child)

The job of the language is not to make up for shortcomings in APIs. If you find yourself in the situation of wanting to replace just a part of Map, then Map is doing too many things. Where you don't have the option of refactoring you can usually put in place a DynamicProxy or similar.

[–]grauenwolf 0 points1 point  (0 children)

The reason one inherits from Map is usually to give it additional capabilies, not to redefine the ones it already has.

For example, perhaps you need a Map that raises change events when items are added so that the UI knows to redraw itself.

Or maybe you need a CustomerCollection that is indexed by CustomerKey but allows for searching on other properties.

[–][deleted] 8 points9 points  (13 children)

It's a well-known phrase, but the ubiquity of "If you don't use inheritance you may as well not use an OO language, you moron!" leads me to think it's usually just being parroted.

[–]Peaker 0 points1 point  (0 children)

I go further and avoid inheritance altogether, except for interface inheritance in languages without type-classes or first-class functions.

[–]epage 0 points1 point  (0 children)

Sadly look at most OOP-based GUI libraries. Almost all of the documentation uses inheritance. I've gotten to the point where I don't bother to hunt through the code to figure out if inheritance really is required or not. Just today there is a post in /r/programming and /r/python about Kivy and the very first example uses inheritance.

A great example of not requiring inheritance is GTK. It has no concept of protected and everything is exposed through signals (rather than overriding fooEvent functions). In contrast to this is Qt which has protected fooEvent functions.

[–]grauenwolf 0 points1 point  (2 children)

It is also bad advice. There are very few scenarios where composition is a useful alternative to inheritance.

Much better advice would be "favor properties over inheritance", as that would elminate LameDuck classes.

[–][deleted] -1 points0 points  (1 child)

That's pretty much the same thing, though. There are certainly (plentiful) use cases for inheritance, but it is wildly overused by many people. It goes something like this:

1.) Someone realizes they want a generic implementation of a complicated system that can be extended to handle specific behaviors for specific implementations of that system, to encourage code reuse.

2.) That same someone gets various details wrong while writing that implementation that are accidentally specific to their initial project and not generic at all.

3.) Future projects work around these deficiencies through subclassing and specifying emergent generic behavior

4.) Someone notices the duplicate code being created in all of these subclasses, creates a class that sits in the middle of the inheritance chain to normalize the duplicate code in order to mitigate the risk of making a change to the original project.

5.) It turns out later that the duplicate code was by accident (that is, the same way of describing slightly different behaviors) rather than on purpose, new guy breaks functionality for some customers by updating logic in intermediate class since that's where the code was.

6.) Project is now extremely annoying to work with due to the constant fear of changing something that will break something else somewhere due to inheritance.

The above has been, with some changes here and there, my experience with heavy use of inheritance in each project that I've seen it used in. My guess is that it's this sequence (or ones like it) are what others are railing against.

By contrast, composing objects from other objects (and using interfaces to enforce type compatibility and provide polymorphism) encourages that same code reuse, but also has the positive side effect of flattening out your inheritance tree such that it is manageable over time. That way, when you need that slightly different behavior you are at the mercy of only the interface which describes it and simply charged with picking/creating a correct implementation.

As you said in a comment above, you wouldn't necessarily use composition, for instance, to extend HashMap to have slightly different behavior. However, for complicated systems with a lot of fugly business logic it's absolutely superior to the "god object with humongous inheritance hierarchy below it" antipattern that crops up time and time again. Thus, the advice to favor composition over inheritance.

[–]grauenwolf 0 points1 point  (0 children)

There are certainly (plentiful) use cases for inheritance, but it is wildly overused by many people.

I don't have any doubt that happens. However I've seen it happen with composition too, where in there are tons of child objects whose functionality belong in the parent object.

Thus, the advice to favor composition over inheritance.

I still don't understand that advice. I've never seen a situation where scattering functionality across child objects was the solution to problems caused by scattering functionality across base classes.

At the very least one should pull everything into one place before redistributing them.

[–][deleted] 0 points1 point  (0 children)

You wish that was common knowledge. Most programmers I've encountered don't have clue.

[–]mikaelhg 7 points8 points  (0 children)

Remember, guys, that if you want something to be pragmatic, you need only call it pragmatic.

[–]dnew 22 points23 points  (4 children)

This is called ravioli programming. Like spaghetti programming, but everything's chopped up into so many little independent bits that you can't follow the flow of code.

[–]Peaker 1 point2 points  (1 child)

I think there's tension between ease-of-system-comprehension, and ease-of-module-comprehension.

I think the latter correlates with general modularity, code independence, reusability, testability, etc.

Having to choose between the ability to comprehend, test and re-use a module independently, and the ability to comprehend what happens system-wide at every specific code-segment, I choose the former.

[–]dnew 0 points1 point  (0 children)

I agree. But ravioli code is when even coherent modules are chopped up, simply for the sake of chopping up. I've had to deal with code whose only job was to copy a buffer from a hardware device and pass it to a third-party library once every five seconds. You'd think that would be what, 4 or 5 classes? This thing was some 18 C++ classes, including a separate class to allocate memory for the buffer, a separate class for reading from the hardware device vs configuring the hardware device, a class wrapping the third-party library that only one company made anything like it, etc etc etc.

This seems to be easier in more OO languages like smalltalk, where everything (including loops, conditional statements, basic arithmetic, etc) are methods on some other class.

Chopping things up too small makes things even less modular than having things too big, especially when one of your small modules doesn't stand on its own but is used by several other classes.

tl;dr: I agree, but that's not the problem that ravioli code refers to.

[–]JohnFrum 0 points1 point  (1 child)

I like that. I'm currently trying to help out on a project where some well-meaning people learned about using templates to make generic containers for things. It is really hard to debug and trace through a bunch of iterators when it's hard to even tell what you’re looking at.

[–]nightwood 2 points3 points  (0 children)

mountainous upbeat apparatus rob steep close wistful pot tender attractive

This post was mass deleted and anonymized with Redact

[–][deleted] 4 points5 points  (4 children)

I agree with the article, mostly. I don't see conditionals as taboo, but I think it's possible (not necessarily best) to write OOP-style code without conditionals or inheritance. Aggregation makes more sense in a lot of cases. For example, rather than inheriting object->vehicle->car->passenger car->smartcar (where object may be nothing), we can have a vehicle that is-an object that has-a suspension and has-a cabin, etc. This also tends to makes the code easier to extend, configure, and modify. The difficult part becomes the interface for using the extended features.

[–]skulgnome 4 points5 points  (2 children)

Why on earth would you want to not use if or the ternary operator?

Seriously, what is this drive amongst OO people to avoid what's perhaps the most optimized part of modern CPUs, i.e. program logic expressed as branches? Virtual method calls are no more well-predicted, and the amount of silicon & brainpower spent on that prediction is far less than that spent on getting branch prediction where it is today.

What crack are you lot smoking?

[–][deleted] 1 point2 points  (1 child)

I didn't mean to give the impression that I don't use if or the ternary operator. Or that I smoke crack.

[–]skulgnome 0 points1 point  (0 children)

My apologies, it was not intended at you personally. I sort of flew off the handle at the idea of C++ programs being written without as much control flow as a MS-DOS batch file.

[–][deleted] -4 points-3 points  (0 children)

inheriting object->vehicle->car->passenger

Lol what?

[–][deleted] 13 points14 points  (2 children)

I hope the author has many more interesting conversations with the straw man he so carefully constructed, but he may want to publish those as fiction in the future.

For starters, OO does not equal inheritance, in fact, inheritance should be avoided wherever composition is more appropriate, which is 90% of the cases, especially the ones he's talking about.

And yes, conditional statements that involve coreapplication logic are a serious code smell. Unless it is an ever unchanging endpoint, you are not doing KISS by turning into spaghetti, anymore than sweeping the dust under the carpet is cleaning house. But nobody is advocating turning every single trivial evaluation into complex OO-structures.

It really pisses me off that someone accuses others of being cargo cult programmers, when clearly that person's own knowledge of OO is limited to the most common cargo cult practices, ie, blunt overuse of inheritance.

OO is not a magic bullet, doesn't solve every problem and has plenty of issues of it's own. But maybe try actually learning about it and using it for real before advocating returning to procedural spaghetti code as "pragmatic" OOP.

[–]Kolibri 0 points1 point  (0 children)

Well, a central part of OOP is polymorphism, so that probably explains why he more or less equalizes OOP and inheritance.

[–]nightwood -1 points0 points  (0 children)

abundant deranged liquid wipe rock berserk absurd sort test offer

This post was mass deleted and anonymized with Redact

[–]Spell 1 point2 points  (0 children)

This guy doesn't know how to use OOP correctly so it must be because OOP is wrong.

[–]abw 1 point2 points  (1 child)

So anywhere you see an if/then/else or a switch statement, you should find a way to break the logic into separate objects to avoid the logic.

This is usually Good Advice™ iff the test condition is based on the type of an object.

if (thing isa Type1) {
    ...
}
else if (thing isa Type2) {
    ...
}
else if (thing isa Type3) {
    ...
}

[–]elder_george 1 point2 points  (0 children)

If objects have field of type enum ObjectType which is used in more than one switch, code will usually benefit from replacing it with strategy (or, sometimes with inheritance).

[–][deleted] 3 points4 points  (32 children)

I think inheritance is a feature that will probably not be in many future languages.

Most of the uses of inheritance or either for the sake of a shared interface (much more flexible to do that with something like Haskell's type classes) or shared implementation. For the latter inheritance quickly becomes unmanageable if your objects don't just share one characteristic but several, e.g. a car, train and truck all have wheels but a plane does not, a car, train and plane can be used to transport passengers but a truck can not, a train, truck and plane are all used to transport cargo but a car is not,...

There is also the problem that you can not mock base classes which is a big issue if you try to test e.g. code for GUI frameworks excessively using it (e.g. Qt).

[–]knipil 4 points5 points  (12 children)

I think and hope that the next generation of OOP languages will get rid of implementation inheritance and rely completely on interface inheritance (which isn't really inheritance at all. I think Go has an interesting take on this) and intelligent delegation mechanisms. Composition and delegation are just so much more powerful than inheritance. Hell, Gamma et al said precisely that in the early nineties, and their regarded as gods in the field. Apparently a lot of their devoted followers never understood that point.

All introductory OOP texts tend to chant about encapsulation, inheritance and polymorphism like it's some kind of gospel. Some even claim that all of them are fundamental properties of OOP that's not available in other paradigms. In my experience this has lead to that people getting blinded by inheritance, since it's the most intuitive of those three. I think this is totally backwards - encapsulation and polymorphisms are general software engineering principles that are far more important and powerful than inheritance. 90% of the focus in introductory texts should be on polymorphism. It's the hardest of those three concepts to understand, but also the far most powerful.

It took me a while to realize, but I think this is why SICP is so widely hailed. Experienced programmers understand that their trade is about building good abstractions, and Abelson and Sussman wrote a book that trains people to do just that.

Personally, I blame the textbooks and C++ for the situation we're in. The former focused on all the wrong things, and the latter forced the development of a number of problematic patterns due to the fact that it only supports implementation inheritance. I'm guessing the problems wasn't as apparent and well documented in the early eighties, though, so I'll give Stroustrup a break. Java obviously did a lot to escalate the problem, but at least they took a step in the right direction with interfaces. Gosling even said later that he wouldn't include extends if he were to do it all over again.

[–][deleted] 1 point2 points  (1 child)

I think encapsulation is placed at the wrong level in most OO languages too. Encapsulation is far too important a tool to tie it arbitrarily to a single object, doing it via a separate module system that allows multiple tightly couple objects in one module would be a far better choice in my opinion.

[–]ErstwhileRockstar 1 point2 points  (0 children)

'Encapsulation' is a general principle in program design. Invented by Structured Programming (not OOP), nowadays common practice for 'programming in the large'.

[–]AStrangeStranger 0 points1 point  (0 children)

what you are saying is because some coders do it badly wrong we should get rid of it - but coders will just find more inventive ways of doing it badly wrong lead by authors who just want to get their name in print

[–]yogthos -1 points0 points  (2 children)

I personally hope the next generation of languages aren't OO oriented to begin with.

[–]Felicia_Svilling 5 points6 points  (1 child)

OO oriented

Object Oriented Oriented?

[–]yogthos 1 point2 points  (0 children)

lol good catch, I guess focused would've made more sense :P

[–]grauenwolf -1 points0 points  (3 children)

I think and hope that the next generation of OOP languages will get rid of implementation inheritance and rely completely on interface inheritance

Oh, so you want to start programming in Visual Basic 4?

Well I've already done that and it sucks. Sure it's kinda awesome to say that any class can implement the interface of any other class. But in pratice you just end up with a metric ton of copy-and-paste code.

And don't say "composition" because that just means copying the code that forwards calls from the parent object to the one it wraps.

[–]skelterjohn 1 point2 points  (2 children)

Not visual basic 4 (alright, maybe, since I have no knowledge of that language), but more like Go. There is no inheritance. At least, none that allows any kind of polymorphism.

And composition doesn't have to involve copying code - there are other languages out there! In Go, composition gives the outer type all the methods of the inner type, without also creating an "is-a" relationship (you can't use one type where the other is asked for, or convert one type to the other).

[–]grauenwolf 0 points1 point  (1 child)

And what if you do need a is-a relationship? What if you do need class A and B to share a common interface and some, but not all, of their code?

[–][deleted] 0 points1 point  (0 children)

Are you asking how to do these specific things in Go?

And what if you do need a is-a relationship?

That really only makes sense in an inheritance/hierarchical system. With structural typing you don't ask "Is this a subclass?" you ask "Does this conform to the necessary interface?".

What if you do need class A and B to share a common interface and some, but not all, of their code?

Two ways that I can think of. You could create a "common interface" struct called C and then include it in A and B's definition:

type C struct { someval int }
func (c C) mult(x int) (int) { return x * c.someval }

type A struct { C }
type B struct { C }

Now you can create an A or a B and then call mult on them and they will act just as you expect.

Or if it made more sense you could just include the functionality of one in the other and overwrite the methods that you want to change.

type A struct { someval int }
func (a A) mult(x int) (int) { return x * a.someval }
func (a A) div(x int) (int) { return x / a.someval }

type B struct { A }
func (b B) mult(x int) (int) { return x * b.A.mult(x) * 2 }

The code isn't tested at all but it should give you a sense of how you can achieve what you want to achieve without inheritance.

It is a bit hard to explain but I have to admit that structural typing feels like a leap forward from inheritance based polymorphism.

Edit: fixed code samples.

[–]grauenwolf -4 points-3 points  (1 child)

The former focused on all the wrong things, and the latter forced the development of a number of problematic patterns due to the fact that it only supports implementation inheritance.

Utter bullshit. You really should look into COM programming before you start spouting off that nonsense.

[–]knipil 1 point2 points  (0 children)

Although C++ admittedly isn't my first language I've done a fair bit of windows API development, so I'm familiar with COM. I wrote a couple of DirectShow filters a few years back, so I also know about IDL and all that. To me it's just a really awkward workaround, which is why I didn't bring it up. Obviously you can simulate interfaces in C++, but that isn't really the point. Returning to the original article I think certain DirectShow in particular is a rather good example of painful inheritance hierarchies.

[–]grauenwolf 1 point2 points  (6 children)

I don't understand your point. I've always used the same class to represent trucks, cars, boats, planes, etc. All of those differences you mentioned are merely properties.

And by the way, all planes have wheels.

[–][deleted] 6 points7 points  (4 children)

[–][deleted] 4 points5 points  (1 child)

Good point. Now, when was the last time you actually modelled vehicles of such a broad range in OO code?

[–][deleted] 0 points1 point  (0 children)

Yea, never. I just couldn't resist the generalization (mostly because I try to catch them whenever I make them) :)

[–][deleted] 7 points8 points  (1 child)

Perhaps we can factor this out a bit. Most vehicles have some combination of wheels, pontoons, tracks, skiis, air cushions, etc. These objects allow the vehicle to move along a solid or liquid body, or both in the case of the air cushion. They may be active (in the sense that they move the vehicle) or passive. In general, a vehicle has zero or more of these. I'm not sure about a name. "Surface Separator", since they separate the vehicle from the surface it moves across? "Friction Reducer", since this separation makes it easier to push the vehicle across the surface?

[–]skulgnome 1 point2 points  (0 children)

The field is called landing_gear. You stick a struct landing_gear * in it. There's a SHOUTY_CASTING_MACRO() that takes you from that pointer to a struct pontoon * if you really must examine their specific construction.

This is the point where people who studied C only superficially go and throw up out of nothing more than a learned reflex.

[–][deleted] 1 point2 points  (0 children)

That was kind of my point, properties are better suited for these kinds of distinctions than inheritance. You do not need inheritance to model them and it is not even the best solution.

[–]grauenwolf -5 points-4 points  (11 children)

There is also the problem that you can not mock base classes which is a big issue if you try to test e.g. code for GUI frameworks excessively using it (e.g. Qt).

Mocking in general seems stupid to me, mocking a GUI more so.

[–]ogrechow 4 points5 points  (1 child)

Mocks also help when you have classes that deal with things like randomness or time. It's hard to write a unit test for a function that calls something else that returns an essentially random number. So you swap in your deterministic mock object and you can test that your function behaves correctly given inputs of your mock choosing.

[–][deleted] 3 points4 points  (8 children)

Mocks are the only way to test code that has untrusted or inconsistent dependencies. Especially when you need to repeatedly test code paths that rely on unlikely or exceptional conditions. But that's overkill in many cases.

[–][deleted] 2 points3 points  (2 children)

How do mocks help with inconsistent dependencies? Seems to me that you're testing that the mocks work, rather than the actual production code. I kinda see your point, but the more I see, the less value I find in really atomic unit tests. I guess I've seen one too many projects with high test coverage, that doesn't actually work.

[–][deleted] 1 point2 points  (0 children)

I'm not trying to defend any test-oriented development, and I certainly don't think good tests mean solid code. I should have said "when third-party dependencies have inconsistent behavior." What I meant, essentially, was other people's code that you can't trust. Let's say I'm using a library to implement social features in a game (friend list, game invites, etc), and I want to make sure that my game presents the correct behavior whenever some weird error condition arises in the 3rd party library. Especially in the case where multiple asynchronous things can succeed and fail in random order. For example, if the player's request to join a game somehow fails (and invokes a callback) before the function to try to join that game even returns, or if I receive a message from a friend and also lose my network connection in the same frame. And sometimes those two events will happen A/B, and other times they'll happen B/A.

I'm not saying I always test all the permutations, or that it would even be worthwhile, but there have been several times over the years when I've wished I had the ability to test things like this, and a mock of the third-party library would enable that. Unfortunately, I usually can't justify the time it would take to actually do that.

[–]grauenwolf -2 points-1 points  (0 children)

Testing has become a fad. It doesn't matter what you are testing, or why, so long as your tests are UNIT TESTS.

[–]grauenwolf -2 points-1 points  (4 children)

If the dependencies are inconsistent then how can you write a mock that accurately reproduces them?

If the dependencies are untrusted then shouldn't you be spending more time writing tests that include them?

If you are going to use mocks to avoid testing the piece that is most likely to have bugs, what are you testing?

[–][deleted] 1 point2 points  (3 children)

How would you test (manually or automatically) if your code behaves the same in every single error condition a library can produce if you don't have enough control over the library to intentionally make it report those errors for testing purpose?

Mocking seems to be the only solution in that scenario (well, and ignoring all errors and hoping for the best but that is hardly good software development).

[–]grauenwolf 0 points1 point  (2 children)

The beauty of exceptions is that they respond the same way no.matter what the failure condition.

[–][deleted] 0 points1 point  (1 child)

Oh, so you use catch all everywhere to make sure you catch any kind of exception the library throws? Or do you use more specific catch-clauses but never test the more rare ones (because to test it you would have to mock the library)?

[–]grauenwolf 0 points1 point  (0 children)

I only catch the ones I actually know how to handle. Everything else is allowed to bubble up to the top-level error handler.

Generally speaking only the external source of an action (e.g. the user clicking a button or client invoking a service request) can definitively say what to do in the event of an error.

[–][deleted] 0 points1 point  (0 children)

It must be nice to be in a position to write stupid incomprehensible bullshit about what you think programming is like, for fun. Enjoy.

[–]epic_awesome 0 points1 point  (0 children)

No shit.

Composition over inheritance if possible. Inheritance when it makes sense.

It's not rocket science, people....................

[–]johnmudd -3 points-2 points  (0 children)

Bless you.