Missing Opportunities for Polymorphism

From WikiContent

(Difference between revisions)
Jump to: navigation, search
Line 1: Line 1:
-
Polymorphism is one of the grand ideas that is fundamental to OO. The word, taken from Greek, means many (''poly'') forms (''morph''). In the context of programming polymorphism refers to many forms of a particular class of objects or method. But polymorphism isn't simply about alternate implementations. Used carefully, polymorphism creates tiny localized execution contexts that let us work without the need for verbose ''if-then-else'' blocks. Being in a context allows us to do the right thing directly, whereas being outside of that context forces us to reconstruct it so that we can then do the right thing. With careful use of alternate implementations, we can capture context that can help us produce less code that is more readable. This is best demonstrated with some code.
+
Polymorphism is one of the grand ideas that is fundamental to OO. The word, taken from Greek, means many (''poly'') forms (''morph''). In the context of programming polymorphism refers to many forms of a particular class of objects or method. But polymorphism isn't simply about alternate implementations. Used carefully, polymorphism creates tiny localized execution contexts that let us work without the need for verbose ''if-then-else'' blocks. Being in a context allows us to do the right thing directly, whereas being outside of that context forces us to reconstruct it so that we can then do the right thing. With careful use of alternate implementations, we can capture context that can help us produce less code that is more readable. This is best demonstrated with some code, such as the following (unrealistically) simple shopping cart:
public class ShoppingCart {
public class ShoppingCart {
Line 10: Line 10:
}
}
-
Listing 1. Unrealistically simple ShoppingCart
+
Let's say our webshop offers items that can be downloaded and items that need to be shipped. Let's build another object that supports these operations:
-
 
+
-
Let's say our webshop offers items that can be downloaded and items that need to be shipped. Let's build another object that supports these operations.
+
public class Shipping {
public class Shipping {
Line 19: Line 17:
}
}
-
Listing 2. Shipping
+
When a client has completed checkout we need to ship the goods:
-
 
+
-
When a client has completed checkout we need to ship the goods.
+
while (!cart.isEmpty()) {
while (!cart.isEmpty()) {
Line 27: Line 23:
shipping.ship(item, ''???'');
shipping.ship(item, ''???'');
}
}
- 
-
Listing 3. Shipping the order.
 
The parameter ''<code>???</code>'' isn't some new fancy Java syntax, it's an indication that we've got a problem. Should I email or snail-mail the item? By the time I get to this point in the code, I've lost the context I need to resolve this question. We could capture method of shipment in a <code>boolean</code> or <code>enum</code> but then we'd need and ''if-then-else'' to reconstruct our understanding of which form of ship needs to be called. Another solution would be create two classes that both extend Item. Lets call these <code>DownloadableItem</code> and <code>GroundItem</code>. Now let's write some code. I'll promote <code>Item</code> to be an interface that supports a single method, <code>ship</code>. To ship the contents of the cart, we will call <code>item.ship(shipper)</code>. Classes <code>DownloadableItem</code> and <code>GroundItem</code> will both implement <code>ship</code>.
The parameter ''<code>???</code>'' isn't some new fancy Java syntax, it's an indication that we've got a problem. Should I email or snail-mail the item? By the time I get to this point in the code, I've lost the context I need to resolve this question. We could capture method of shipment in a <code>boolean</code> or <code>enum</code> but then we'd need and ''if-then-else'' to reconstruct our understanding of which form of ship needs to be called. Another solution would be create two classes that both extend Item. Lets call these <code>DownloadableItem</code> and <code>GroundItem</code>. Now let's write some code. I'll promote <code>Item</code> to be an interface that supports a single method, <code>ship</code>. To ship the contents of the cart, we will call <code>item.ship(shipper)</code>. Classes <code>DownloadableItem</code> and <code>GroundItem</code> will both implement <code>ship</code>.
Line 43: Line 37:
}
}
}
}
- 
-
Listing 4. Specialization of Item
 
Each item knows if it can be downloaded or if ground shipment is required. There is no need to reconstruct lost context. This information has been captured at the time of order. In this example, <code>DownloadableItem</code> and <code>GroundItem</code> provide a micro execution context that lets us get on with it.
Each item knows if it can be downloaded or if ground shipment is required. There is no need to reconstruct lost context. This information has been captured at the time of order. In this example, <code>DownloadableItem</code> and <code>GroundItem</code> provide a micro execution context that lets us get on with it.
Line 50: Line 42:
The code above is representative of the command and the double dispatch patterns. Effective use of both of these pattern rely on careful use of polymorphism. This is why they often play well together. What is noticeably missing are ''if-then-else'' blocks.
The code above is representative of the command and the double dispatch patterns. Effective use of both of these pattern rely on careful use of polymorphism. This is why they often play well together. What is noticeably missing are ''if-then-else'' blocks.
-
While there are cases where it's much more practical to use if-then-else instead of polymorphism, it is more often the case that a more polymorphic coding style will yield a smaller, more readable and less fragile code base. The number of missed opportunities lies in the number of ''if-then-else'' statements we can find in our code.
+
While there are cases where it's much more practical to use ''if-then-else'' instead of polymorphism, it is more often the case that a more polymorphic coding style will yield a smaller, more readable and less fragile code base. The number of missed opportunities lies in the number of such ''if-then-else'' statements we can find in our code.
By [[Kirk Pepperdine]]
By [[Kirk Pepperdine]]

Revision as of 18:59, 5 July 2009

Polymorphism is one of the grand ideas that is fundamental to OO. The word, taken from Greek, means many (poly) forms (morph). In the context of programming polymorphism refers to many forms of a particular class of objects or method. But polymorphism isn't simply about alternate implementations. Used carefully, polymorphism creates tiny localized execution contexts that let us work without the need for verbose if-then-else blocks. Being in a context allows us to do the right thing directly, whereas being outside of that context forces us to reconstruct it so that we can then do the right thing. With careful use of alternate implementations, we can capture context that can help us produce less code that is more readable. This is best demonstrated with some code, such as the following (unrealistically) simple shopping cart:

public class ShoppingCart {
    private HashMap<Item,Item> cart = new HashMap<Item,Item>();
    public void put(Item item) { bag.cart(item, item); }
    public Item take(Item itemToTake) { return cart.remove(itemToTake); }
    public boolean look(Item itemToMatch) { return cart.containsKey(itemToMatch); }
    public Item next() { /* find and item and "take" it */ }
    public boolean isEmpty() { return cart.isEmpty(); }
}

Let's say our webshop offers items that can be downloaded and items that need to be shipped. Let's build another object that supports these operations:

public class Shipping {
    public boolean ship(Item item, GroundAddress address) { ... }
    public boolean ship(Item item, EMailAddress address { ... }
}

When a client has completed checkout we need to ship the goods:

while (!cart.isEmpty()) {
    Item item = cart.next();
    shipping.ship(item, ???);
}

The parameter ??? isn't some new fancy Java syntax, it's an indication that we've got a problem. Should I email or snail-mail the item? By the time I get to this point in the code, I've lost the context I need to resolve this question. We could capture method of shipment in a boolean or enum but then we'd need and if-then-else to reconstruct our understanding of which form of ship needs to be called. Another solution would be create two classes that both extend Item. Lets call these DownloadableItem and GroundItem. Now let's write some code. I'll promote Item to be an interface that supports a single method, ship. To ship the contents of the cart, we will call item.ship(shipper). Classes DownloadableItem and GroundItem will both implement ship.

public class DownloadableItem implements Item {
    public boolean ship(Shipping shipper) {
        shipper.ship(this, customer.getEmailAddress());
    }
}

public class GroundItem implements Item {
    public boolean ship(Shipping shipper) {
        shipper.ship(this, customer.getGroundAddress());
    }
}

Each item knows if it can be downloaded or if ground shipment is required. There is no need to reconstruct lost context. This information has been captured at the time of order. In this example, DownloadableItem and GroundItem provide a micro execution context that lets us get on with it.

The code above is representative of the command and the double dispatch patterns. Effective use of both of these pattern rely on careful use of polymorphism. This is why they often play well together. What is noticeably missing are if-then-else blocks.

While there are cases where it's much more practical to use if-then-else instead of polymorphism, it is more often the case that a more polymorphic coding style will yield a smaller, more readable and less fragile code base. The number of missed opportunities lies in the number of such if-then-else statements we can find in our code.

By Kirk Pepperdine

This work is licensed under a Creative Commons Attribution 3


Back to 97 Things Every Programmer Should Know home page

Personal tools