Software Development : Polymorphism and Low Coupling in Java

Many moons ago, I wrote an article on software development using polymorphism in Java. The reason for that paper was that I was encountering too often code that was poorly designed due to a minimal understanding of the Object Oriented concepts. The result was often spaghetti code that was hard to maintain. I dig this paper from the past with the hope that it can be useful to at least one person.

Please note that this paper can be applied to other languages such as Objective-C for developing iPad or iPhone Apps. Interfaces are then just renamed “Protocole”. The principle remains exactly the same. Software development with Object Oriented technologies is best done, not only by developers who understand the language they use but more importantly by developers understanding fully the Design Best Practices.

Without Interfaces, Java would not offer full polymorphism. This is due to the fact that it is a strongly typed language. This article investigates how interfaces are the key to implement polymorphic behaviour in Java.
The case study in this artivle uses a real project situation in order to make it more easily understandable. The article slowly introduces the problem and solution in a didactic perspective.

Table of contents

Java is a strongly typed language

Java is a strongly typed language. What does this mean?

The compiler always need to know what type of object it is dealing with. As soon as a method is called on an object, the compiler needs to check that the object can accept and process this message. This makes Java safe at compile time and also stops the programmer from debugging careless mistakes.

For instance, consider a class Foo such as:

class Foo {
  public void doStuff(){
  }

}

UseFoo class cannot have a method like:

class UseFoo {
  private Foo foo = new Foo();

  void doIt(){
    Foo f = new Foo();
    f.doOtherStuff(); //doOtherStuff() is unknown for a Foo object
  }
}

 

The compiler will refuse to compile UseFoo.doIt(). This is because the type of f is taken in account at compile time and therefore must be defined at this point. For a non typed language like Smalltalk, this code would compile without any problem. You would have problems at run time, when the system tries to send the message doOtherStuff() to the foo object.

As an aside, it should be noted that java is strongly typed not just to prevent programmers from making mistakes but also for applying the java security model, however this would be the topic for another article.

A simple polymorphic situation

Let’s imagine I am writing software for a financial department. The department’s primary function is to pay wages and charge customers. So as a first step I am checking the type of objects I am dealing with. These classes are created by another development team, specifically in charge of this area of the business domain. I have no real control over the class development but I need to know about these classes to complete my development.

What are these classes ? This project is following a Unified Process and we are in the first iteration.

Two relevant business classes have been identified: SalariedEmployee and Contractor. They have been recognised as being specialised types of Employee. An employee has to be paid at one point (hopefully) and its abstraction, the Employee class, will therefore need to contain a getBankDetails() method.
SalariedEmployee and Contractor are overriding getBankDetails(). They are now ready to be paid!

I am responsible for developing the Accountant class. The role of this object is to pay whoever has to be paid. In the first place: Salaried employess and contractors.

If I write a method pay() taking as an argument the person I need to pay, I have to respect 2 conditions:

  1. The type of the argument has to be unique, even if I have 2 different objects.
  2. The process for paying the person has to be the same. Therefore I want to call the same method on each object I receive in order to get their bank details.

The solution to this is to have a method as a common root for Contractor and SalariedEmployee and to have the common behaviour defined in this root. This is exactly what we have! So the pay method in the Accountant class would be implemented as follows:

class Accountant {
   void pay(Employee employee){
       String details = employee.getBankDetails();
       processPayment(details);
   }
}

 

At this point we have solved our first iteration problem. We use polymorphism to implement the payment and we are very happy with that.
Unfortunately, in your project you will probably quickly arrive at the next situation, a bit more tricky and worse – with no straightforward solution.

Extended polymorphic need

The first iteration of my project is running and it is time to think about the next stage. In the requirements, we have discovered that the process for paying bills to cater for companies, for paying third party companies in general, has to be part of the system as well. So, the business objects team has added a few extra classes in their domain model.

In order to use these classes in my pay() method, I need to ensure that they respond to thegetBankDetails() message. This is not a problem, and the team in charge is pleased to help me by adding this method at the root level, in ThirdPartCompany class.

Software Development : Polymorphism And Java Interface

Let’s now work on the Accountant class. I have to update the pay method in order to be able to payThirdPartCompany objects as well as employees. I need to change it in the following way:

class Accountant {
   void pay(EmployeeOrThirdPartCompany personToPay){
       String details = personToPay.getBankDetails();
       processPayment(details);
   }
}

 

The problem in Java is that the compiler wants to know the type of what it is dealing with.

How can we say something like “Employee or ThirdPartCompany”?

There is only one way to do that: to find a common root between Employee and ThirdPartCompany. But these objects have little in common. You cannot really say that an Employee is a kind of ThirdPartCompany and vice versa (cf. OO rules about inheritance).
So, currently, there is no solution for our simple problem in Java. This is due to the strongly typed language. In Smalltalk, you would simply write something like (Smalltalk code):

pay: personToPay
  details:= personToPay getBankDetails.
  self processPayment: details.

 

There is no problem as the compiler is only checking that the method getBankDetails exists somewhere in the system. There would be no problem at runtime as long as you pass into personToPay an object which can answer togetbankDetails.

So, knowing that you cannot solve this problem with inheritance and that polymorphism is a requirement for OO languages, there must be a solution in Java if it is an OO language. This solution is to use an interface.

Full polymorphism thanks to Interfaces

So what is an Interface?

Interfaces can be seen as contracts followed by classes. Basically, it is a set of APIs (the contractual agreement) that the class is respecting, the class is indicating to the world: “I am respecting this contract. I can be trusted!”.

How does it work technically?

An Interface is a type in Java, like a class. But unlike a class it doesn’t contain any implementation. The whole purpose of an Interface is to define a set of APIs (the contractual agreement).
The fact that an Interface is considered as a type is a key point. We’ll see why.

First of all, let us write this famous contract that our Accountant so desperately wants to see. We will call it “PayableByAccountant”. This will be an Interface:

 


public interface PayableByAccountant { String getBankDetails(); }

 

From there, the Accountant will consider that “he” doesn’t care about who wants to be paid, but he is now only interested in one thing: these objects have to respect the PayableByAccountant Interface (Contract). If they do, he will pay them. If they don’t, he will keep the money!

The new Accountant pay() method is:

class Accountant {
   void pay(PayableByAccountant personToPay){
       String details = personToPay.getBankDetails();
       processPayment(details);
   }
}

 

At this point, the Accountant is not dealing anymore with Employees or Companies or whatever. The Accountant is dealing with PayableByAccountant objects. It becomes irrelevant for the Accountant to know in detail with what type of object he is really dealing with as the only thing of importance is that they respect thePayableByAccountant contract containing the famous getBankDetails() method.

 

Now we are almost done. The next step is for the objects who want to be paid by the Accountant to guarantee that they respect this contract. They do so by “implementing” the Interface using the implements keyword:

public class ThirdPartCompany implements PayableByAccountant {
    public String getBankDetails() {
    }
}

public abstract class Employee implements PayableByAccountant {
 public abstract String getBankDetails();
}

 

At this point a ThirdPartCompany object or any of its subclasses is a PayableByAccountant object, and anEmployee object or any of its subclasses is a PayableByAccountant object as well. Because of this, the Accountant can deal with any of them and any instance of a class implementing PayableByAccountant can be passed to the Accountant object. They will all be treated the same and the compiler will never complain again!

Design improvement

On top of having solved our problem, we also have extended the future possibilities for our software. By providing an interface to anyone who wants to be paid, we can add in the future iterations new classes that we have not thought about yet and they will all be payable without changing a single line of the Accountant!

We have, by the way, improved the quality of our design by lowering the coupling as the Accountant doesn’t have to be aware of the other classes that might need to be paid.

Conclusion

Without interfaces, this polymorphic behaviour could not have been implemented at all. This is why I do not consider interfaces as mainly an elegant way to have multiple-inheritance without the usual C++ problems; but I rather consider interfaces as the Java solution for implementing the basic OO concept called Polymorphism.

 

Liemur provides Software Development Services within UK and with its nearshore branch in Budapest.