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.
Bookmark or Share this article:
Related Articles on Robert Green's DIY:
2 Comments
Post a comment here or discuss this and other topics in the forumsre: Object Oriented Programming Explained - Part 2 - Polymorphi
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.
re: Object Oriented Programming Explained - Part 2 - Polymorphi
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.
Post new comment