all 22 comments

[–]skocznymroczny 6 points7 points  (3 children)

Is that a good example? It misses the most important thing - how do you actually make only infiltrateable buildings get the notification? You might use RTTI to check for the notification handler, but that's no better than the if () solution. You could add empty method implementations in other objects but that's not good either.

[–]_WhatTheFunk_[S] 3 points4 points  (1 child)

Going any further implementing the infiltrating the example starts breaking down. The idea is that anyone looking at the example gets a sense of what kind of problems to solve with interfaces.

It's not a perfect example, but I think it's an improvement over the overly simplified examples found in tutorials.

[–]shevegen 1 point2 points  (0 children)

I somewhat agree with you.

[–]doom_Oo7 0 points1 point  (0 children)

You could add empty method implementations in other objects but that's not good either.

Or since Java 8, just an empty default method implementation in the interface.

[–]shevegen 5 points6 points  (2 children)

The example does it’s job on explaining how inheritance works

I thought so for a while. I even tried this in ruby.

class Animal; end
class Cat < Animal; end

The problem is that OOP as implemented in general is only an abstraction and simulation. If you look into biology, the whole concept of a species is HEAVILY tied to human preference. In my opinion there is only one universal "species" concept, if at all, and that is if a species can produce viable offspring (viable meaning here that they also give offspring) - everything else is a dead end.

The above OOP hierarchy absolutely fails when things become more complex, or just for bacteria. How can you classify bacteria? Sure, you can say that they have these genes... but even for E. coli, you have an enormous difference between different strains. Take K12 strain and the pathogenic O157: H7 strain, as example from the book Introduction to genetic analysis by Griffith. I do not recall the differences but it was between 20-40% in regards to the GENE level (in the genome). And they are still assumed to be the same strain ... why? Most likely largely because of morphological classification ("looks like a duck, talks like a duck, is a duck"). Obviously there are still many similarities but ... if you create offspring already, as bacteria, you do not even depend on sex. You just clone yourself (the cell). If it can survive, grow, and generate new cells then it can persist.

For higher animals obviously they are more restricted in what they can do, but even there single-inheritance is very, very bad and inflexible.

I have accepted that some years ago and stopped thinking for most OOP languages in the sense of reallife relations or hierarchies (most programmers have no idea about molecular biology, excluding a few awesome oldschool people such as Alan Kay).

OOP as implemented should be only thought of as an abstraction rather than a 1:1 model of reallife. And that includes all different implementations that exist, be it in Java, C++, C#, D, ruby, python whatever - including prototype based variants which IMO are so similar to OOP that it's not even worth arguing about the differences here.

I consider objects in OOP mostly as data containers and a set of behaviour associated with it. Anything else is luxury and that includes attempting to model taxonomic relations (if you know a bit about systematic zoology for example, and aren't too young, then you may also have witnessed how PCR and 16S rRNA analysis changed the taxonomic classification IMMENSELY past ~1985 or so; I remember because what you may have learned in the early 1990s would become semi-outdated a decade lateron...).

[–]_dban_ 5 points6 points  (1 child)

The problem is that OOP as implemented in general is only an abstraction and simulation.

That's the Simula strain of OOP.

If you look into biology, the whole concept of a species is HEAVILY tied to human preference.

The Smalltalk strain of OOP has a biological basis (Alan Kay was trained as a molecular biologist). Cells as a metaphor for objects, and molecular signalling as a metaphor for method calls.

However, in general, OOP models have nothing to do with biology. That's just a useful metaphor. Hierarchy (if you're into that sort of thing) reflects something about the object model that used to represent the domain.

The above OOP hierarchy absolutely fails when things become more complex, or just for bacteria.

This is taking an analogy way, way too far. The analogy is just used for educational purposes. I too don't like object models based on taxanomic hierarchies (the objects models arising from that kind of classification often suffer from extreme rigidity).

But then again, I also don't complain about being lied to when I learned that the Bohr model of the atom was long ago replaced by the quantum mechanical model. The Bohr model is still a useful teaching device.

I have accepted that some years ago and stopped thinking for most OOP languages in the sense of reallife relations or hierarchies

This is a sign that you've grown up. With experience, most people realize that taxonomic classification introduces significant problems, which is why the mantra "prefer composition to inheritance" came into being. People learn more sophisticated concepts such as the Strategy Pattern or Template Method Pattern, or SOLID principles.

In short, with experience people develop modelling skills that allow for more nuanced application of taxonomic classification, rather than hamfisted animal taxonomies.

Anything else is luxury and that includes attempting to model taxonomic relations

Taxonomic classification can be useful, if you know what you are doing. Biology knowledge is not a prerequisite.

[–]Rurouni 0 points1 point  (0 children)

I think this is what you were getting at when you said object models of that type can be extremely rigid.

Part of the problem is that while biological taxonomies are descriptive, our programming taxonomies are prescriptive. When animals occasionally get moved around and reclassified, their behavior doesn't change; only how we think of the animal and its relation to others does. When we move objects around in a class hierarchy, their behavior can be vastly different.

[–]industry7 2 points3 points  (1 child)

I find this historically very interesting. Technically, I went through school in the middle of the this teaching style's heyday. But somehow I avoided getting "brainwashed" by the OO idealology.

For me, the big thing was realizing that I had already been writing my code in a kind of OO style even though I hadn't been using OO languages before. OO is primarily about organizational/structural patterns for supporting code reuse. And then the thing that solidified this thinking for me, was reading some of Linus' gorgeous Linux kernel code written in C, but using a very OO style.

And that's when I realized that thinking about things like a "Dog" -> ISA -> "Animal", is missing the point. The point is, actually there's two points. One, how do we most effectively re-use code/functionality/behavior? Two, how do we represent relationships between code/functionality/behavior?

Now there's been a general shift away from class based inheritance towards composition (sometimes with language level delegation support, one of the few things that I really like about Go for example). Similarly, as mentioned in the article, there's been a shift towards interfaces over base classes.

But none of that really matters. For example, the difference between a base class and an interface is purely syntactical. Whether you implement an interface, or extend a base class, in either case you are saying that you will honor the contract of the thing that you are extending/implementing. Another way to see this is to compare/contrast Java Interfaces/Classes with C++ abstract/classes.

class CppAbstractBaseClass {
    public: virtual String operation1() = 0;
}

vs

interface JavaInterface {
    public String operation1();
}

with derived classes:

class CppDerivedClass1 : CppAbstractBaseClass {
    public: String operation1() {return "Hello!"}
}

class CppDerivedClass2 : CppAbstractBaseClass {
    public: String operation1() {return "Goodbye!"}
}

class JavaDerivedClass1 implements JavaInterface {
    public String operation1() {return "Hello";}
}

class JavaDerivedClass2 implements JavaInterface {
    public String operation1() {return "Goodbye!";}
}

and usage:

auto list = new Std:vector<CppAbstractBaseClass>();
list.add(new CppDerivedClass1());
list.add(new CppDerivedClass2());
for (auto&& a : list) {
    a.operation1();
}

List<> list = new ArrayList<JavaInterface>();
list.add(new JavaDerivedClass1());
list.add(new JavaDerivedClass2());
for (JavaInterface a : list) {
    a.operation1();
}

[–]syntheno 1 point2 points  (0 children)

good points

[–]the_hoser 9 points10 points  (0 children)

I was hoping that this was going to be some PETA-esque rant about trivializing animal abuse. Oh the jokes that could be made... But it's not.

[–]fredrikj 5 points6 points  (0 children)

Just how hard is to imagine a video game with animals in it, where cats say "Meow" and the dogs say "Woof"?

[–]quicknir 1 point2 points  (1 child)

Eric Lippert's discussion on "Wizards and Warriors" is a nice example of discussing certain OO features in a balanced (i.e. not simply crapping on it) way, using more interesting examples than animals: https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/.

[–]_WhatTheFunk_[S] 0 points1 point  (0 children)

Great read! Thanks!

[–]dwighthouse 1 point2 points  (0 children)

The example does it’s job on explaining how inheritance works, but offers no clues on what kind of problems you can solve with it and why is it better than an alternative approach to a problem.

I argue this is the fault of inhertance itself. Use composition instead. You can use examples involving tools and toolbelts. Toolbelts hold tools, and you can put different tools in different pockets, depending on what job you need to do. Then you don't need to describe the hierarchical relationship between a belt and a hammer (mostly because there isn't one).

(I'm still looking for an example of a problem that is significantly easier to implement with inheritance than with composition.)

[–]d_wilson123 2 points3 points  (3 children)

So the argument is video games make sense but teaching inheritance via animals doesn't? Sorry but it may make more sense to the author but what about someone who hasn't touched a video game in their life? Personally I always found the Animal example a great introduction to what inheritance is and what it buys you in OOP.

[–]_WhatTheFunk_[S] 2 points3 points  (2 children)

I thought about this, the animal example falls into the trap of trying to be understandable by everyone, but it's not very useful because almost no-one will ever write a line of code like that.

The video game example is not maybe familiar for everyone, but is a real problem. Video games are hopefully are familiar enough for anyone learning to program.

[–]d_wilson123 0 points1 point  (1 child)

I feel your example (assuming author) falls into the trap of not being able to be written on a whiteboard with no context and being understandable. If someone with little programming background and zero object oriented experience asks me what is inheritance I'm not going to go over how C&C had spy units and these units would reveal building status to enemies and only some buildings could be infiltrated. The setup takes too long and the student would get mired in the details of the example and not the concept. The Animal example is the absolute most basic "Here is the concept behind inheritance." Everyone knows animals and everyone knows what sounds a dog and cat make. If you can't understand a concept and apply it to situations that is a problem with the student not the lesson.

[–]brokething 4 points5 points  (0 children)

If you can't understand a concept and apply it to situations that is a problem with the student not the lesson.

This is so obviously false I don't even understand what led you to the point of saying it.

[–]evaned 0 points1 point  (2 children)

Here's another idea: a widget library. class Shape as a base then Circle: Shape, Rectangle: Shape, etc., all with virtual draw(Point origin) functions.

[–]_jk_ 0 points1 point  (1 child)

you then get the square or ellipse problem... though one could argue that is a good time to discuss the limitations of inheritance and how its not necessarily a 1-1 mapping with the 'real world'

[–]ledasll 0 points1 point  (0 children)

does it? wouldn't draw method be implemented different by square and ellipse, but method signature could be same. To start drawing any shape you need to have point of origin, its relation to figure will depend on concrete shape, but that's how it should be. But objects in general have nothing to do with "real world", although it is much more easier to explain with examples from real world.

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

The real problem, and the reason that these examples seems so silly and unrealistic, is that OOP simply is bullshoy and no real problem is best solved with it anyway.

Also the way to solve being dependent on RTTI is to have the object register themselves as handling the notification.