This is part 3 in the series “A Beginner’s Guide to Object-Oriented Programming with Apex” and will cover the aspect of Polymorphism. For other posts in this series see the links below.
A Beginner’s Guide to Object-Oriented Programming with Apex
- Introduction
- Encapsulation
- Abstraction
- Polymorphism (this post)
What the hell is Polymorphism?
Polymorphism has the funkiest name and is my favourite aspect of OOP. If done correctly you will feel like a proper genius.
Originating from Greek words, polymorphism more or less means “many forms”. This gives away very little unless you know polymorphism quite well but essentially polymorphism describes the ability of a type e.g. a set of differing Apex classes, to be used in similar ways without knowing much about those classes. An immediate example on the platform is the Type.format() static method available on Integers, DateTime, etc**. Although each of the Types represents something different this method does a similar thing for each i.e. turns them into a String but does this in a different way for each type. Which brings me to a definition of Polymorphism:
Polymorphism describes a pattern in object-oriented programming in which classes have different functionality while sharing a common interface.
Let’s take this academic statement and make it more concrete by looking at an example.
**I’ve assumed that these “primitives” are subclasses of a shared superclass or implementations of a shared interface.
How do we implement Polymorphism?
Polymorphism in Apex requires an Apex Interface to be implemented or Class that can be extended by other Apex classes e.g. Virtual or Abstract classes. The implementing or extending classes can then be used in a predictable, consistent way even though their types are different from one another. Time for an example.
Imagine that we need to display the original price and discounted price for a set of completely different products. We want to use a single Visualforce table to do this and with the bare minimum of VF code whilst modelling these products in a reusable and extensible way. We start off by defining an interface like so:
public interface Product {
/**
Any class that implements this interface must
have methods with the following signatures
**/
Decimal getDiscount();
Decimal getPrice();
String getName();
}
Any class that implements this interface has to implement methods that match the two method signatures that have been defined.
Now we create two categories of products, one for fruit and the other for finance.
/** Fruit Products Class **/
public with sharing class FruitProduct implements Product {
private final Decimal discountedRate = 0.85;
private Decimal price;
private String name;
// The Constructor
public FruitProduct(String name, Decimal price) {
this.price = price;
this.name = name;
}
// Price method implemented due to contract set by interface
public Decimal getPrice() {
return price;
}
// Discount method implemented due to contract set by interface
public Decimal getDiscount() {
return price * discountedRate;
}
// Method to retrieve product name
public String getName() {
return name;
}
}
/** Financial Products Class **/
public with sharing class FinancialProduct Implements Product {
private final Decimal discountedRate = 0.45;
private final Integer hiddenCost = 100;
private Decimal price;
private String name;
// The Constructor
public FinancialProduct(String name, Decimal price) {
this.price = price;
this.name = name;
}
// Price method implemented due to contract set by interface
public Decimal getPrice() {
return price;
}
// Discount method implemented due to contract set by interface
public Decimal getDiscount() {
return (price + hiddenCost) * discountedRate;
}
// Method to retrieve product name
public String getName() {
return name;
}
}
Notice that these classes both implement the same interface and therefore some of their methods have the same signatures. However the calculation of the discount is different in each class! As the author of these classes the inner workings of the getDiscount methods could do anything that you can imagine – they could use other classes and methods, make web service callouts etc. without impacting their intended polymorphic behaviour.
But how will we use two different types of Apex class in one VF table? The answer is in their Polymorphic capabilities! So how can we apply this? Let’s start with the page.
<apex:page controller="ProductController">
<apex:pageBlock>
<apex:pageBlockSection>
<apex:pageBlockTable value="{!allProducts}" var="p">
<apex:column>
<apex:facet name="header">Product Name</apex:facet>
<apex:outputText value="{!p.name}" />
</apex:column>
<apex:column>
<apex:facet name="header">Price</apex:facet>
<apex:outputText value="£{!p.price}" />
</apex:column>
<apex:column>
<apex:facet name="header">Discounted Price</apex:facet>
<apex:outputText value="£{!p.discount}" />
</apex:column>
</apex:pageBlockTable>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:page>
Notice how we only iterate over a single list! Let’s see how this is done in the controller.
public with sharing class ProductController {
// This list can hold any product that implements the Product interface
public List<Product> allProducts{get;set;}
public ProductController() {
allProducts = new List<Product>();
// Instantiate some fruit and financial products
FruitProduct banana = new FruitProduct('Banana', 1);
FruitProduct dragonFruit = new FruitProduct('Dragon Fruit', 2.50);
FinancialProduct mortgage = new FinancialProduct('Mortgage', 100);
FinancialProduct spreadBet = new FinancialProduct('Spread Bet', 5);
// And add them to the list
allProducts.add(banana);
allProducts.add(dragonFruit);
allProducts.add(mortgage);
allProducts.add(spreadBet);
}
}
Looking inside the controller we see that the list allProducts is of type Product which isn’t a class but an interface. By doing this we are saying, “create a list that is able to contain any instantiated Apex class that implements the Product interface” i.e. the list can hold any Financial or Fruit Product, or in fact any other class that might later be created that implements this interface. The cool thing is that our Visualforce page (or developer) doesn’t have to know the exact type of class that’s being passed, all it knows is that a particular method will be available for it to call. This is very powerful and is a common use for polymorphism.
Another common use is within ISV applications that need to be extensible. Such an application might provide several interfaces that an external person/company could implement to extend the application’s functionality. For example, the application may have a set of interfaces that describe a particular way of creating classes that allow the external party to implement a custom outbound (from SFDC) integration from within the app. This way the creators of the ISV app don’t have to code every conceivable integration into their app but instead allow others the ability to do this without exposing the inner workings of their app to the world!
Why Should I Use Polymorphism?
Polymorphism is amazingly powerful once you’ve got the hang of it. It requires you to plan your application in a fair amount of detail and results in code that is loosely coupled, easy to extend, faster to write and generally more robust. As if that wasn’t enough it also makes you feel really smart!
This concludes the series on object-oriented programming and I’m sure that I’ve raised more questions than provided answers. The topic is huge and I encourage you to grab a book on OOP because it is key to becoming an expert software developer no matter which language you use.
If you’d like a bit more info on the topic of OOP and are around for DF14 look out for my talk where I’ll work through an example of applying OOP to code when connecting to external APIs.
“Any class that implements this interface has to implement methods that match the two method signatures” — getName has to be implemented as well, correct?