Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

The more I learn about different programming paradigms, such as functional programming, the more I begin to question the wisdom of OOP concepts like inheritance and polymorphism. I first learned about inheritance and polymorphism in school, and at the time polymorphism seemed like a wonderful way to write generic code that allowed for easy extensibility.

But in the face of duck typing (both dynamic and static) and functional features such as higher-order functions, I've begun to look at inheritance and polymorphism as imposing an unnecessary restriction based on a fragile set of relationships between objects. The general idea behind polymorphism is that you write a function once, and later you can add new functionality to your program without changing the original function - all you need to do is create another derived class which implements the necessary methods.

But this is a lot simpler to achieve through duck typing, whether it's in a dynamic language like Python, or a static language like C++.

As an example, consider the following Python function, followed by its static C++ equivalent:

def foo(obj):
   obj.doSomething()

template <class Obj>
void foo(Obj& obj)
{
   obj.doSomething();
}

The OOP equivalent would be something like the following Java code:

public void foo(DoSomethingable obj)
{
  obj.doSomething();
}

The major difference, of course, is that the Java version requires the creation of an interface or an inheritance hierarchy before it will work. The Java version thus involves more work, and is less flexible. Additionally, I find that most real-world inheritance hierarchies are somewhat unstable. We've all seen the contrived examples of Shapes and Animals, but in the real world, as business requirements change and new features are added, it's difficult to get any work done before you need to really stretch the "is-a" relationship between sub-classes, or else remodel/refactor your hierarchy to include further base-classes or interfaces in order to accommodate new requirements. With duck typing, you don't need to worry about modeling anything - you just worry about the functionality you need.

Yet, inheritance and polymorphism are so popular that I doubt it would be much of an exaggeration to call them the dominant strategy for extensibility and code reuse. So why are inheritance and polymorphism so wildly successful? Am I overlooking some serious advantages that inheritance/polymorphism have over duck typing?

share|improve this question
    
I'm not a Python expert so I have to ask: what would happen if obj doesn't have a doSomething method? Is an exception raised? Does nothing happen? –  FrustratedWithFormsDesigner Apr 14 '11 at 19:51
6  
@Frustrated: A very clear, specific exception is raised. –  dsimcha Apr 14 '11 at 19:52
8  
Isn't duck typing just polymorphism taken up to eleven? My guess is that you're not frustrated with OOP but with its statically typed incarnations. I can understand that, really, but that doesn't make OOP as a whole a bad idea. –  delnan Apr 14 '11 at 20:16
2  
First read "widely" as "wildly"... –  user8685 Apr 14 '11 at 21:20
add comment

9 Answers

up vote 19 down vote accepted

I mostly agree with you, but for fun I'll play Devil's Advocate. Explicit interfaces give a single place to look for an explicitly, formally specified contract, telling you what a type is supposed to do. This can be important when you're not the only developer on a project.

Furthermore, these explicit interfaces can be implemented more efficiently than duck typing. A virtual function call has barely more overhead than a normal function call, except that it can't be inlined. Duck typing has substantial overhead. C++-style structural typing (using templates) can generate huge amounts of object file bloat (since each instantiation is independent at the object file level) and doesn't work when you need polymorphism at runtime, not compile time.

Bottom line: I agree that Java-style inheritance and polymorphism can be a PITA and alternatives should be used more often, but it still has its advantages.

share|improve this answer
add comment

I've begun to look at inheritance and polymorphism as imposing an unnecessary restriction based on a fragile set of relationships between objects.

Why?

Inheritance (with or without duck typing) assures reuse of a common feature. If it's common, you can assure that it's reused consistently in subclasses.

That's all. There's no "unnecessary restriction". It's a simplification.

Polymorphism, similarly, is what "duck typing" means. Same methods. Lots of classes with an identical interface, yet different implementations.

It's not a restriction. It's a simplification.

share|improve this answer
add comment

Inheritance is abused, but so is duck typing. Both can and do lead to problems.

With strong typing you get many "unit tests" done at compile time. With duck typing you often have to write them.

share|improve this answer
    
Your comment about testing only applies to dynamic duck typing. C++-style static duck typing (with templates) gives you errors at compile time. (Although, granted C++ template errors are pretty hard to decipher, but that's a different issue entirely.) –  Channel72 Apr 15 '11 at 4:14
add comment

The good thing about learning things in school is that you do learn them. The not-so-good thing is you may accept them a little too dogmatically, without understanding when they are useful and when not.

Then, if you have learned it dogmatically, you may later "rebel" just as dogmatically in the other direction. That's not good either.

As with any such ideas, it's best to take a pragmatic approach. Develop an understanding of where they fit and where they don't. And ignore all the ways they've been oversold.

share|improve this answer
add comment

Yes, static typing and interfaces are restrictions. But everything since structured programming was invented (ie "goto considered harmful") has been about constraining us. Uncle Bob has an excellent take on this in his video blog.

Now, one can argue that constriction is bad, but on the other hand it brings order, control and familiarity to an otherwise very complex topic.

Relaxing constraints by (re-)introducing dynamic typing and even direct memory access is very powerful concept, but it can also make it harder for many programmers to deal with. Especially programmers used to relying on the compiler and type safety for much of their work.

share|improve this answer
add comment

Inheritance is a very strong relationship between two classes. I don't think Java has any stronger. Therefore, you should only use it when you mean it. Public inheritance is a "is-a" relationship, not a "usually is-a". It's really, really easy to overuse inheritance and wind up with a mess. In many cases, inheritance is used to represent "has-a" or "takes-functionality-from-a", and that's typically better done by composition.

Polymorphism is a direct consequence of the "is-a" relationship. If Derived inherits from Base, then every Derived "is-a" Base, and therefore you can use a Derived wherever you'd use a Base. If this doesn't make sense in an inheritance hierarchy, then the hierarchy is wrong and there's probably too much inheritance going on.

Duck typing is a neat feature, but the compiler won't warn you if you're going to misuse it. If you don't want to have to handle exceptions at run-time, you need to assure yourself that you're getting the right results every time. It may be easier just to define a static inheritance hierarchy.

I'm not a real fan of static typing (I consider it to often be a form of premature optimization), but it does eliminate some classes of errors, and many people think those classes are well worth eliminating.

If you like dynamic typing and duck typing better than static typing and defined inheritance hierarchies, that's fine. However, the Java way does have its advantages.

share|improve this answer
add comment

Inheritance and polymorphism are widely used because they work, for certain kinds of programming problems.

It's not that they're widely taught in schools, that's backwards: they're widely taught in schools because people (aka the market) found that they worked better than the old tools, and so schools began teaching them. [Anecdote: when I was first learning OOP, it was extremely difficult to find a college that taught any OOP language. Ten years later, it was difficult to find a college that did not teach an OOP language.]

You said:

The general idea behind polymorphism is that you write a function once, and later you can add new functionality to your program without changing the original function - all you need to do is create another derived class which implements the necessary methods

I say:

No, It Isn't

What you describe is not polymorphism, but inheritance. No wonder you're having OOP problems! ;-)

Back up a step: polymorphism is a benefit of message-passing; it simply means that each object is free to respond to a message in its own way.

So...Duck Typing is (or rather, enables) polymorphism

The gist of your question seems to be not that you don't understand OOP or that you dislike it, but that you don't like defining interfaces. That's fine, and as long as you're careful things will work fine. The drawback is that if you've made a mistake - omitted a method, for example - you won't find out until run-time.

That's a static vs dynamic thing, which is a discussion as old as Lisp, and not limited to OOP.

share|improve this answer
    
+1 for "duck typing is polymorphism". Polymorphism and inheritance have been subconsciously chained together for far too long, and strict static typing is the only real reason they ever had to be. –  cHao Dec 2 '13 at 0:01
add comment

The truth lies somewhere in the middle. I like how C# 4.0 being statically typed language supports "duck typing" by "dynamic" keyword.

share|improve this answer
add comment

I've noticed that the more I use C#'s closures the less I'm doing traditional OOP. Inheritance used to be the only way to easily share implementation, so I think it was often over-used and the conception bounds were pushed too far.

While you can usually use closures to do most of what you'd do with inheritance, it can also get ugly.

Basically it's a right-tool-for-the-job situation: traditional OOP can work really well when you have a model that suits it, and closures can work really well when you don't.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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