18

I am trying to understand how Polymorphism is used in a real life project, but I can only find the classical example (or something similar to it) of having an Animal parent class with a methodspeak(), and many child classes that override this method, and now you can call the method speak() on any of the child objects, for example:

Animal animal;

animal = dog;
animal.speak();

animal = cat;
animal.speak();
5
  • 2
  • 3
    The collections, that you see and use in every single day, itself is enough to understand what polymorphism is. But how to use polymorphism effectively in problem solving is a skill you gain mosty by experience and not merely by discussing. Go on and get your hands dirty. Jul 22 '18 at 17:07
  • If you have a set of types that will all support some kind of minimum interface (e.g., a set of objects that need to be drawn), an interface is usually a good fit to hide the differences between objects from the call to draw it. Also if you're making (or working with) an API that has methods that can service a base object and a significant number of types that inherit from it more or less the same way, polymorphism may be the best way to abstract the differences between those types.
    – jrh
    Jul 22 '18 at 18:29
  • In general if you're frequently making overloaded methods to handle different types and the code is similar, or if you're writing if(x is SomeType) DoSomething() often, it may be worth using polymorphism. For me polymorphism is a decision similar to when to make a separate method, if I found that I repeated the code a few times I usually refactor it into a method, and if I find that I'm either making if object is this type do this code often, it might be worth refactoring and adding an interface or class.
    – jrh
    Jul 22 '18 at 18:34
36

Stream is great example of polymorphism.

Stream represents a "sequence of bytes that can be read or written". But this sequence can come from file, memory, or many kinds of network connections. Or it can serve as decorator, that wraps existing stream and transforms the bytes in some way, like encryption or compression.

This way, the client who uses Stream doesn't need to care where the bytes come from. Just that they can be read in sequence.

Some would say Stream is wrong example of polymorphism, because it defines many "features" that it's implementors don't support, like network stream only allowing reading or writing, but not both at the same time. Or lack of seeking. But that is only question of complexity, as Stream can be subdivided into many parts that could be implemented independently.

5
  • 2
    In languages with multiple and virtual inheritance like C++, this example can even demonstrate the "dreaded diamond" pattern... by deriving input and output stream classes from a base stream class, and extending both to create an I/O stream
    – gyre
    Jul 22 '18 at 13:32
  • 2
    @gyre And done well, there’s no reason to “dread” the diamond pattern. Needing to be aware of the opposite counterpart in the diamond and not causing name conflicts with it is important, and a challenge, and annoying, and reason to avoid the diamond pattern where practicable... but don’t go too far dreading it when simply having, say, a naming convention could solve the problems.
    – KRyan
    Jul 22 '18 at 14:30
  • +1 Streams are my all-time favourite polymorphism example. I don't even attempt to teach people the flawed 'animal, mammal, dog' model anymore, Streams do a but better job.
    – Pharap
    Jul 22 '18 at 15:31
  • @KRyan I wasn't expressing my own thoughts by calling it the "dreaded diamond," I've just heard it referred to as such. I completely agree; I think it's something every developer should be able to wrap their head around and use appropriately.
    – gyre
    Jul 22 '18 at 19:56
  • @gyre Oh, yeah, I actually got that; that’s why I started with “and,” to indicate it was an extension of your thought, rather than a contradiction.
    – KRyan
    Jul 22 '18 at 19:57
8

A typical games related example would be a base class Entity, providing common members such as draw() or update().

For a more pure data oriented example there could be a base class Serializable providing a common saveToStream() and loadFromStream().

6

There are different kinds of polymorphism, the one of interest is usually runtime polymorphism/dynamic dispatch.

A very high-level description of runtime polymorphism is that a method call does different things depending on the runtime type of its arguments: the object itself is responsible for resolving a method call. This allows for a huge amount of flexibility.

One of the most common ways to use this flexibility is for dependency injection, e.g. so that I can switch between different implementations or to inject mock objects for testing. If I know in advance that there will only be a limited number of possible choices I could try to hardcode them with conditionals, e.g.:

void foo() {
  if (isTesting) {
    ... // do mock stuff
  } else {
    ... // do normal stuff
  }
}

This makes the code hard to follow. The alternative is to introduce an interface for that foo-operation and write a normal implementation and a mock implementation of that interface, and “injecting” to desired implementation at runtime. “Dependency injection” is a complicated term for “passing the correct object as an argument”.

As a real-world example, I am currently working on a kind machine-learning problem. I have an algorithm that requires a prediction model. But I want to try out different machine learning algorithms. So I defined an interface. What do I need from my prediction model? Given some input sample, the prediction and its errors:

interface Model {
  def predict(sample) -> (prediction: float, std: float);
}

My algorithm takes a factory function that trains a model:

def my_algorithm(..., train_model: (observations) -> Model, ...) {
  ...
  Model model = train_model(observations);
  ...
  y, std = model.predict(x)
  ...
}

I now have various implementations of the model interface and can benchmark them against each other. One of these implementations actually takes two other models and combines them into a boosted model. So thanks to this interface:

  • my algorithm doesn't need to know about specific models in advance,
  • I can easily swap out models, and
  • I have a lot of flexibility in implementing my models.

A classic use case of polymorphism is in GUIs. In a GUI framework like Java AWT/Swing/… there are different components. The component interface/base class describes actions such as painting itself to the screen, or reacting to mouse clicks. Many components are containers that manage sub-components. How might such a container draw itself?

void paint(Graphics g) {
  super.paint(g);
  for (Component child : this.subComponents)
    child.paint(g);
}

Here, the container doesn't need to know about the exact types of the subcomponents in advance – as long as they conform to the Component interface the container can simply call the polymorphic paint() method. This gives me the freedom to extend the AWT class hierarchy with arbitrary new components.

There are many recurring problems throughout software development that can be solved by applying polymorphism as a technique. These recurring problem–solution pairs are called design patterns, and some of them are collected in the book of the same name. In the terms of that book, my injected machine learning model would be a strategy that I use to “define a family of algorithms, encapsulate each one, and make them interchangeable”. The Java-AWT example where a component can contain sub-components is an example of a composite.

But not every design needs to use polymorphism (beyond enabling dependency injection for unit testing, which is a really good use case). Most problems are otherwise very static. As a consequence, classes and methods are often not used for polymorphism, but simply as convenient namespaces and for the pretty method call syntax. E.g. many developers prefer method calls like account.getBalance() over a largely equivalent function call Account_getBalance(account). That's a perfectly fine approach, it's just that many “method” calls have nothing to do with polymorphism.

6

You see lots of inheritance and polymorphism in most UI toolkits.

For example, in the JavaFX UI toolkit, Button inherits from ButtonBase which inherits from Labeled which inherits from Control which inherits from Region which inherits from Parent which inherits from Node which inherits from Object. Many layers override some methods from the previous ones.

When you want that button to appear on the screen, then you add it to a Pane, which can accept anything inheriting from Node as a child. But how does a Pane know what to do with a Button when it just sees it as a generic Node object? That object could be anything. The pane can do that because the Button redefines the methods of Node with any button-specific logic. The pane just calls the methods defined in Node and leaves the rest to the object itself. This is a perfect example of applied polymorphism.

UI toolkits have a very high real world significance, making them useful to teach for both academic and practical reasons.

However, UI toolkits also have a significant drawback: They tend to be huge. When a neophyte software engineer tries to understand the internal workings of a common UI framework, they will often encounter over a hundred classes, most of them serving very esoteric purposes. "What the heck is a ReadOnlyJavaBeanLongPropertyBuilder? Is it important? Do I have to understand what it's good for?" Beginners can easily get lost in that rabbit hole. So they might either flee in terror or stay on the surface where they just learn the syntax and try to not think too hard about what's actually happening under the hood.

3

Although there are already nice examples here, another one is to replace animals with devices:

  • Device can be powerOn(), powerOff(), setSleep() and can getSerialNumber().
  • SensorDevice can do all this, and provide polymorphic functions such as getMeasuredDimension(), getMeasure(), alertAt(threashhold) and autoTest().
  • of course, getMeasure() will not be implemented in the same way for a temperature sensor, a light detector, a sound detector or a volumetric sensor. And of course, each of these more specialized sensors may have some additional functions available.
2

Presentation is a very common application, perhaps the most common one being ToString(). Which is basically Animal.Speak(): You tell an object to manifest itself.

More generally speaking you tell an object to "do its thing". Think Save, Load, Initialize, Dispose, ProcessData, GetStatus.

2

My first practical usage of polymorphism was an implementation of Heap in java.

I had base class with implementation of methods insert, removeTop where the difference between max and min Heap would only be how method compare works.

abstract class Heap {  

 abstract boolean compare ( int x , int y );

 boolean insert(int x ) { ... }

 int removeTop() { ... }
}

So when i wanted to have MaxHeap and MinHeap I could just use inheritance.

class MaxHeap extends Heap {

   MaxHeap(int maxSize) {super(maxSize);}

   @Override
   boolean compare(int x, int y) {
       return x>y; // x<y for minHeap
   }
}
1

Here's a real life scenario for web app/database table polymorphism:

I use Ruby on Rails to develop web apps, and one thing that a lot of my projects have in common is the ability to upload files (photos, PDFs, etc.). So for example, a User might have multiple profile pictures, and a Product might also have many product images. Both have the behavior of uploading and storing images, as well as resizing, generating thumbnails, etc. In order to stay DRY and share the behavior for Picture, we want to make Picture polymorphic so that it can belong to both User and Product.

In Rails I would design my models as such:

class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

class User < ApplicationRecord
  has_many :pictures, as: :imageable
end

class Product < ApplicationRecord
  has_many :pictures, as: :imageable
end

and a database migration to create the pictures table:

class CreatePictures < ActiveRecord::Migration[5.0]
  def change
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      t.timestamps
    end

    add_index :pictures, [:imageable_type, :imageable_id]
  end
end

The columns imageable_id and imageable_type are used by Rails internally. Basically, imageable_type holds the name of the class ("User", "Product", etc.), and imageable_id is the id of the associated record. So imageable_type = "User" and imageable_id = 1 would be the record in the users table with id = 1.

This allows us to do things like user.pictures to access the user's pictures, as well as product.pictures to get a product's pictures. Then, all of the picture-related behavior is encapsulated in the Photo class (and not a separate class for each model that needs photos), so things are kept DRY.

More reading: Rails polymorphic associations.

0

There are many sorting algorithms available like bubble sort, insertion sort, quick sort, heap sort etc. and they have different complexity and which one is optimum to use depends on various factors ( ex: size of the array)

Client provided with sort interface only concern about providing array as an input and then receive sorted array. During runtime depending on certain factors appropriate sorting implementation can be used. This is one of real world example of where polymorphism is used.

What I described above is an example of runtime polymorphism whereas method overloading is an example of compile time polymorphsim where complier depending upon i/p and o/p parameter types and number of parameters bind caller with right method at complie time itself.

Hope this clarifies.

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