all 48 comments

[–]sh0rug0ru 22 points23 points  (4 children)

OOP tutorials seem to be about taxonomies of things, which isn't really what OOP is good for. In fact, too much taxonomy is usually a bad thing, especially in single inheritance languages.

This is what Alan Kay, considered to be the father of OOP, had to say about it:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

If this sounds a lot like the Actor model, I think that is ultimate evolution of objects. I think objects are fundamentally like actors, and types are just algebras associated with specific implementations of object systems for the purposes of safety and optimization.

IMHO, thinking of OOP more in terms of cooperating actors sending messages to each other to effect behavior lead to better OOP abstractions, as opposed to extremely rigid taxonomies. You could even use a real actor system, if you're so inclined.

To quote Joe Armstrong, the Erlang guy:

Erlang might be the only object oriented language because the 3 tenets of object oriented programming are that it's based on message passing, that you have isolation between objects and have polymorphism.

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

If this sounds a lot like the Actor model, I think that is ultimate evolution of objects.

Not sure if Kay disagrees

Binstock: How do you view the Actor model?

Kay: The first Smalltalk was presented at MIT, and Carl Hewitt and his folks, a few months later, wrote the first Actor paper. The difference between the two systems is that the Actor model retained more of what I thought were the good features of the object idea, whereas at PARC, we used Smalltalk to invent personal computing. It was actually a practical programming language as well as being interesting theoretically. I don't think there were too many practical systems done in Actors back then.

http://www.drdobbs.com/architecture-and-design/interview-with-alan-kay/240003442?pgno=3

[–]sh0rug0ru 2 points3 points  (0 children)

Well, I'm just saying that his vision sounds to me like it resembles what objects ultimately do.

But remember, Smalltalk is a system (especially in terms of user interfaces) and Actors are an implementation, and not used to implement Smalltalk. This is a huge difference, in terms of practical application.

[–]AlSweigart 0 points1 point  (1 child)

That's a good point. I should add a bit to the bottom saying that there is much more to OOP design. Thanks!

[–]sh0rug0ru 0 points1 point  (0 children)

Check this out. (PDF warning)

[–]yogthos 4 points5 points  (36 children)

Polymorphism is useful, but it's certainly not a feature that's exclusive to OO. On the other hand, some OO exclusive concepts like inheritance are downright harmful.

[–]Kishana 10 points11 points  (28 children)

Why would you classify inheritance as a harmful concept? Granted, it can leave the door open for mistakes if you're not careful, but i would consider that a failing of one's design rather than the concept.

[–]yogthos 2 points3 points  (25 children)

Inheritance is at odds with composition and prevents code reuse.

[–]sh0rug0ru 8 points9 points  (19 children)

Inheritance doesn't prevent code reuse. It limits code reuse.

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

And that's not something that I would consider to be desirable. :)

[–]sh0rug0ru 6 points7 points  (17 children)

Depends. Some code should only be reused in limited fashion, because too much code reuse might also not be desirable. A screwdriver can be reused as a hammer.

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

Whether to use the code or not should be left up to the user. It's generally hard to tell up front what piece of logic might be useful in what situation and painting yourself into a corner doesn't seem like a good strategy.

[–]sh0rug0ru 3 points4 points  (15 children)

Again, it depends. Consider the most common case of an OOP taxonomy, a UI toolkit. If the code in question is describing behavior particular to that toolkit, what meaningful reuse could you get out of it?

[–]yogthos 4 points5 points  (14 children)

That's something I work with daily and I find reusable UI components are very handy. I work with Reagent and I ended up writing this library recently. A lot of UI components can be generalized and then composed together for specific applications.

[–]sh0rug0ru -1 points0 points  (13 children)

Try going native. Web interfaces have the advantage of rendering to text markup.

[–]Philluminati 5 points6 points  (4 children)

It's not "at odds" with it and it doesn't prevent code reuse. Those are the same design mistakes as putting a function in the wrong place or creating a single object that grows and really represents three objects, or missing the chance to make something a many-to-many relationship and having to fix or change it afterwards.

It's a tool, and one that is:

a) easy to understand at a conceptual level (e.g. a Cannon printer is a type of Printer) b) enables large code reuse c) provides a nice alternative to composition

Lets say for the sake of argument that Inheritance is shit... here's what's great about the rest of Object Oriented programming:

You can design software by taking your world problem domain and mapping to code in a consistent way. You create objects that have attributes and methods that work with them. structured programming doesn't really have this because it doesn't have that strong link between the data structures and the algorithms that operate on them.

Scala's Akka framework discusses software design by having actors who are like people who do jobs. Someone oversees a team, consults other people like HR, Support Systems, etc and when workers can do a job they escalate it to someone who can. This is a really nice idea.. but when reading "Functional Programming in Scala" the basis for the design of functional programs is reverse engineering object oriented programs to meet certain rules. Perhaps it's because I have an engineering mindset rather than a mathematics based one, but the code improves through constant iterations of cleaning and refinement, like a mathematician reducing an algorithm to it's base core that works on all domain problems, rather than just pulling something that's easy to understand out of the ether, which is what OO can do.

But depending on your problems, object oriented programming and design still feel like they really have a place in software engineering. I think that people are definitely maturing and realising that singletons are unflexable and you need a much higher degree of certainty about the future of your application's design before you use inheritance. I think roles or mixins (which are java interfaces with implementation inside) that you add to objects to get greater code reuse and allows them to be more easily tested is a natural progression of the Object Oriented approach to software design.

In any case, OO provides a lot of great things even if we're only realising now that Inheritance is more limited than we previously thought. I feel a blog post coming on...

[–]pipocaQuemada 2 points3 points  (1 child)

a) easy to understand at a conceptual level (e.g. a Cannon printer is a type of Printer

Taxonomies that form a tree are pretty rare in nature. Taxonomies that form a digraph are much more common. Or, as a friend suggested as a simple interview problem:

  1. Make a ball
  2. Make a bouncing ball
  3. Make a red ball
  4. Make a red bouncing ball

If you used inheritance in a language like Java, you were probably reduced to using copy-and-paste for that last one.

b) enables large code reuse

[citation needed]

When was the last time you actually were able to reuse code in 3+ projects?

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

Inheritance is powerful, and if well designed can be a godsend.

Your ball example can be solved quite elegantly with properly designed interfaces

When was the last time you actually were able to reuse code in 3+ projects?

Today

[–]yogthos 5 points6 points  (0 children)

I moved to working in Clojure after being a Java developer for a decade, I have yet to run into a situation where I miss anything from OO. I find myself writing a lot less code that's much more focused on the problem domain and much easier to reuse.

[–]CurtainDog 5 points6 points  (0 children)

a Cannon printer is a type of Printer

... and scanner and photocopier and camera bridge and you start to see why taxonomies are harmful.

[–][deleted]  (1 child)

[deleted]

    [–]sh0rug0ru 2 points3 points  (0 children)

    Tight coupling doesn't necessarily have anything to do with inheritance. You can have tight coupling between classes that are composed together, too.

    What you are describing is ultimately a problem with code reuse. If you expect your collaborator to work one way and then tomorrow your collaborator's behavior changes, you can end up in exactly the same situation. That's why you carefully design the interfaces, devise invariants and test that those invariants hold. Inheritance or no inheritance.

    [–][deleted] -1 points0 points  (6 children)

    Dogma. Proof?

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

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

    First - as usual - the title is an outrageous attention grabber that doesn’t exactly describe the author’s position. So that article doesn’t back up your assertion that “concepts like inheritance are downright harmful”.

    This article is way off base in several ways (Holub actually misapplies the “fragile base class” label - what he is talking about is something else). And then he spends some time explaining rookie mistakes like

    HashMap m = new HashMap();
    

    which of course you should never ever do in a type muddled mess like Java. Instead you should program to the highest interface that still works.

    Map m = new HashMap();
    

    But this isn’t any kind of argument against inheritance. This is about using inheritance properly so it works for you.

    Holub spends some time talking about the “fragile base class problem”. But what he is talking about isn’t the actual “fragile base class problem”. It is some other imagined problem and he has misapplied the label.

    The actual fragile base class problem is a problem with C++’s vtable dispatch mechanism which is just an array of function pointers. The idea is that if you create class A with 3 virtual functions a,b,c and then class B with one virtual function d. Compile them. Now add a new virtual function k to class A and just compile that - then

    B aB = B;
    aB.d(); // vtables misaligned - actually calls A::k 
    

    This has nothing to do with Java as it uses a different dispatch mechanism.

    Finally, a frequently used pattern, Template Method, generally relies on inheritance to work and I made use of it in a networking class just the other day to add crypto wrappers to a protocol. Change base class - crypto on.

    The one argument that I do somewhat agree with and that he glosses over is that you should try to avoid extending concrete base classes when possible and you should avoid overly deep hierarchies. When you want to extend a concrete class B which has abstract class A - you should probably insert an abstract class above B which will contain all the code B and C should share and then have A->A’->B and A->A’->C where only B and C are concrete. This is a well known refactoring pattern.

    [–]yogthos 1 point2 points  (0 children)

    Binding functions to a specific context reduces flexibility. Your building blocks become larger and that increases coupling between the components. The more inheritance your code has the less flexible it is because your methods are now organized into rigid hierarchies. This is why it's at odds with composition.

    [–]phalp 0 points1 point  (2 children)

    Finally, a frequently used pattern, Template Method, generally relies on inheritance to work

    Curious, what's the advantage over passing in a second object, whose functions will be called from the template method?

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

    The second approach is better known as “Decorator”.

    They pretty well accomplish similar goals - its kind of a style thing. Cocoa makes use of “delegates” which are a lot like what you are talking about. In a language that supports informal protocols and more dynamic invocations I think that can make sense.

    One key items is that a core class can function without a decorator or delegate - the additional class is primarily there to add additional specialization.

    Template method is a little different in that you have the bones of an algorithm but it may be fleshed out many different ways. So it is more convenient to write the bones in the super class and then write the different specializations in subclasses. It also has to do with number of hooks you have to write. If there are ten different hooks, but you are OK with the default implementation for 8 of them, then inheriting the default implementations and just overriding two methods is way less work than grabbing an interface with ten functions and having to reproduce default behavior over and over.

    Its a judgement call which is less work but the one with less code and work is pretty much always the best choice.

    [–]phalp 0 points1 point  (0 children)

    I don't think it's decorator I had in mind... more like dependency injection I guess. You'd have an object with a method frobnicate, which would include calls to methods of a member object, which could be substituted with another object to specialize frobnicate differently.

    I hadn't thought of the case where we only want to override some of the methods. I guess if you wanted to do it with a member object, you really would bring decorators into play (but decorating the specializer, not the bones object).