Object Oriented Programming Explained - Part 2 - Polymorphism

| | Comments (2) | TrackBacks (3)
Wikipedia defines polymorphism as "the ability of objects belonging to different types to respond to method calls of the same name, each one according to an appropriate type-specific behavior."  There is nothing too fancy about polymorphism other than it's name.  All it means in layman' s terms is that you can define some common behavior for multiple types of objects and have a common codebase which may or may not care about the different variations on a type.  A good analogy would be the biological taxonomy system.  You may remember this from high school.  It has Kingdom, Phylum, Class, Order, Family, Genus and Species.  If we were to model all living things according to their taxonomy, then we could have software which behaves much like a dog kennel.  The dog kennel takes domesticated dogs.  There are many specific breeds of dogs but the kennel knows that any domesticated 4 legged dog will do and will be compatible with their kennel.  We could say this dog is polymorphic in a way.  Not only is it a domesticated dog, but it's a carnivorous canine which is an animal.  Does our software support all animals?  Maybe.  If so, this dog would work there too.
If the dog kennel analogy didn't quite make sense to you, I'll speak to you purely in programming terms.  Let's say we're developing an application in which we need to add a data cache for performance reasons.  Our application already has a several classes which we use to model the problem domain.  I'll give you a concrete example of the problem and the solution.

Let's say we have a system that needs to be able to render images (jpg, gif, png, etc..) to a window and there is nothing written that does this yet.  We know each different file type is an image, but they all use different encodings.  This is a perfect case for polymorphism.  We have a common goal (display all images to the window) but different types of images to display with different algorithms needed for each.

// this class represents an image.
Class Image {
    private File imageFile;
    private ImageType imageType;
   
    // constructor - called on new()
    public Image(File imageFile) {
       this.imageFile = imageFile;
       if (imageFile.extension == "JPG") {
          imageType = ImageType.JPEG;
       } else if (imageFile.extension == "GIF") {
          imageType = ImageType.GIF;
       } else if (imageFile.extension == "PNG") {
          imageType = ImageType.PNG;
       }
    }

    // returns the type of image this is
    public ImageType getImageType() { return imageType; }

    // gets the appropriate image renderer for this image type
    public ImageRenderer getImageRenderer() {
       if (imageType == ImageType.JPEG) {
          return new JpegRenderer(imageFile);
       } else if (imageType == ImageType.GIF) {
          return new GifRenderer(imageFile);
       } else if (imageType == ImageType.PNG) {
          return new PngRenderer(imageFile);
       }
}

// this is the abstract class for an image renderer.  It can not render anything by itself.
Abstract Class ImageRenderer {
    public ImageRenderer(File imageFile) {
       this.imageFile = imageFile;
    }

    public void renderImage() {
       Data data = readDataFromDisk(imageFile);
       renderImageData(data);
    }
  
    protected abstract void renderImageData(Data imageData);

    protected Data readDataFromDisk(File imageFile) {
       //read bytes, common file operation...
       return data;
    }
}

Class JpegRenderer extends ImageRenderer {
    protected void renderImage(Data imageData) { // Jpeg Specific Decoding Here }
}

Class GifRenderer extends ImageRenderer {
    protected void renderImage(Data imageData) { // Gif Specific Decoding Here }
}

Class PngRenderer extends ImageRenderer {
    protected void renderImage(Data imageData) { // Png Specific Decoding Here }
}
      

Image Rendering Code:

// point to the image file the user specified
File imageFile = new File ("/path/to/some/user/image/file");
// create a new "image" based on this
Image image = new Image(imageFile);
// get the renderer for the image
ImageRenderer renderer = image.getImageRenderer();
// render the image
renderer.renderImage();


Read through the code and pay particular attention to how simple the Image Rendering part is.  All that's done from that point is an image is loaded, the render is referenced and invoked.  From that point it looks very, very simple and that's because all of the hard parts are abstracted out and delegated to their own classes which handle the more complex stuff.  From here you can see that all that's required to finish this implementation is to get a working decoding algorithm for each image type and put them in the respective ImageRenderers. 

This works because we knew the common things about an image were that they are able to be rendered onto the screen.  The type of file is chosen when an Image is instantiated and the Renderer is picked based on that.  This is extensible because to add support for additional image types, you just need to add an ImageRenderer supporting it and add a few lines in to the Image class.

This sort of abstraction is the basis of polymorphism.  ImageRenderer is the polymorphic class here, not Image.  It is a design decision to implement it that way, as I'm sure some would prefer to also abstract out Image and then have JpegImage, GifImage, PngImage, etc.  That's not entirely necessary as the only difference at that level is which renderer is called, because again, all of the difficult and unique work is performed in the Renderer.

To understand this better, say to yourself, "Only an ImageRenderer is needed.  All ImageRenderers Render Images.  A JpegRenderer IS an ImageRenderer so it can Render Images."

Organization is key here.  It doesn't matter that I haven't filled out the decoding sections.  We all know that it's a bunch of algorithms that decode the file and turn it into RGB data.  The design is what's important.

Related Articles:

Object Oriented Programming Explained - Part 1 - The Basics
How to start writing software:  Web Applications
How I became a professional software developer

Categories

3 TrackBacks

Listed below are links to blogs that reference this entry: Object Oriented Programming Explained - Part 2 - Polymorphism.

TrackBack URL for this entry: http://www.rbgrn.net/blog/mt/mt-tb.cgi/53

Object Oriented (OO) languages are a staple of the development community, however many people continue to argue against them, citing that they are inefficient, useless, confusing and unnecessary.  There are many real benefits to using OO even for ... Read More

Hibernate is an excellent tool which saves many projects from substantial amounts of code.  Gone are the days of writing massive datasource layers full of obnoxious JDBC code.  As gravy you also get simple query objects, caching, connection p... Read More

If you have a need to use the command pattern and want to have a standard interface but with type safe parameters, I have come up with a solution.  This is one of the most interesting uses (or hacks) of... Read More

2 Comments

martin.kukan said:

from my point of view you're violating SRP in your example and also you created a dependency between image and imagerenderer and that is not a good practice.
I don't understand why you mix factory for creating imagerenderer inside Image class.

Robert Green said:

I believe you are looking a little too deep into my "designs." This was an example that I typed out just to show how Polymorphism works, not to show best practices or anything else.

Leave a comment


Type the characters you see in the picture above.

Contact

If you find these articles helpful, please donate!
Every dollar is appreciated.