Step 4: Polymorphic References
Not Started

If you ask a developer what Polymorphism means, they'll become an expert in Ancient Greek and declare it means "having many forms".

Polymorphism is achieved through polymorphic references, which are variables that are capable of referring to multiple object types.

Monomorphic References

When we first learned about variables and objects, we learned about the different types.

String firstname = 'Saman'; Boolean isCoolGuy = true;

These are monomorphic references. We can only assign strings to the variable firstName, and we can only assign a boolean to isCoolGuy. Trying to assign anything else would cause compilation errors. Polymorphic references work differently.

Binding and Polymorphic References

IProduct productA; // ... // code that assigns productA productA.calculateTotalPrice();

productA is a polymorphic reference that has the ability to reference various object types at different times. However, it is not capable of referencing just any object; instead, it can only reference objects that are derived from it, specifically any class that implements the IProduct interface.

Assuming HardwareProduct, SoftwareProduct, and ProfessionalServicesProduct all implemented IProduct, you could build a list, and dynamically call the calcualteTotalPrice method on each one:

List<IProduct> productList = new List<IProduct>(); productList.add(new HardwareProduct('Servers')); productList.add(new SoftwareProduct('Licenses')); productList.add(new ProfessionalServicesProduct('Consultant')); Decimal sum = 0; for(IProduct p : productList){ sum += p.calculateTotalPrice(); }

Linking a method call to its corresponding method implementation is called binding. Most of the time, binding in Apex happens at compile time. For polymorphic reference, the object reference is not known until the line of code is executed, so binding has to wait until runtime. This is called late binding or dynamic binding. Dynamic binding is possible because of the contract established between the concrete class and the interface.

IAccount Polymorphic Reference

In our ongoing example, you created a hierarchy by creating a subclass, BusinessAccount, that implements the IAccount interface. Now, we can create our polymorphic reference to achieve polymorphism.

These two code blocks both compile and have the same result:

BusinessAccount bAccount = new BusinessAccount(); // a monomorphic reference bAccount.placeOrder(customerOrder); PersonAccount pAccount = new PersonAccount(); // another monomorphic reference pAccount.placeOrder(customerOrder2);

IAccount anyAccount = new BusinessAccount(); // Polymorphic reference established anyAccount.placeOrder(customerOrder); // Calls the appropriate implementation from BusinessAccount anyAccount = new PersonAccount(); anyAccount.placeOrder(customerOrder2); // Calls the appropriate implementation from PersonAccount

The second code block shows we can use the IAccount type to reference both PersonAccount objects and BusinessAccount objects. This comes in handy with method parameters and arguments. Let's take advantage of this in the OrderController.

Challenge

Assuming you now have the two classes:

public class BusinessAccount implements IAccount { Account accRecord; public BusinessAccount(Account a){ this.accRecord = a; } public void placeOrder(Order o){ // place order logic specific to Business Accounts } }

and

public class PersonAccount implements IAccount { Account accRecord; public PersonAccount(Account a){ this.accRecord = a; } public void placeOrder(Order o){ // place order logic specific to Person Accounts } }

Refactor the OrderController code by eliminating the overloaded methods. Instead, create one method named createOrder which accepts a single IAccount and an Order object. Call the placeOrder method on IAccount.