Interface design pattern and its use in Java

In this tutorial we will show you what is Interface Pattern and when we consider this Interface Pattern to use in our application design.

The interface pattern can be used to design a set of service provider classes that offer the same service so that a client object can use different classes of service provider objects in a seamless manner without having to alter the client implementation.

The functionality of an object-oriented system is encapsulated in the form of a set of objects. These objects provide services either on their own or by interacting with other objects.

An object that requests a service from another object is referred to as a client object.

From the below figure we can see the client object assumes that the service provider objects corresponding to a specific service request are always of the same class type and directly interacts with the service provider object. This type of direct interaction ties the client with a specific class type for a given service request.
Interface Pattern in Java
The above approach works fine when there is only one type of objects from a class offering a given service, but may not be adequate when there are more than one type of objects from multiple classes that provide the same service required by the client as shown below.

The client will not be able to make use of the different classes of service provider objects in a seamless manner because the client expects the service provider to be always of the same class type. Hence it requires changes to the design and implementation of the client and greatly reduces the re-usability of the client by other objects.
Interface Pattern in Java

From the above problem we can find in such cases, the Interface pattern can be used to design better implementation with different service provider classes that offer the same service to enable the client object to use different classes of service provider objects with little or no need for altering the client code.

Using the Interface pattern, the common services offered by different service provider classes can be abstracted out in a separate interface.

Each of the service provider classes can be designed as implementers of this common interface.

From the below figure we can see that objects of different service provider classes can be treated as objects of the interface type. This enables the client to use different types of service provider objects in a seamless manner without requiring any changes even when a new service provider is designed as part of the class hierarchy.
Interface Pattern in Java

Example

Let’s build an application to calculate and display salaries of the different employees of an organization on the basis of category of designations as shown below:

DescriptionCategories
Developers, Architect, ConsultantTechnologies
Recruitment Manager, Human ResourcesHR
Executives, Vice PresidentsDirectors

Let’s assume that the application should consider only those employees whose designation is part of category Technologies.

The salary calculation functionality for all employees in the category Technologies can be described by the below class:

public class TechnologiesSalary {
	double basicSalary;
	double overTimeSalary;
	public TechnologiesSalary(double basicSalary, double overTimeSalary) {
		this.basicSalary = basicSalary;
		this.overTimeSalary = overTimeSalary;
	}
	public double getTotalSalary() {
		return (basicSalary + overTimeSalary);
	}
}

The class representation of an employee, in its simplest form, can be designed as in the following listing with two attributes: the employee name and the category of designation.

public class Employee {
	String employeeName;
	TechnologiesSalary techSalary;
	public Employee(String employeeName, TechnologiesSalary techSalary) {
		this.employeeName = employeeName;
		this.techSalary = techSalary;
	}
	public void displayDetails() {
		System.out.println("Name : " + employeeName);
		System.out.println("salary : " + techSalary.getTotalSalary());
	}
}

A client object can configure an Employee object with values for the employee name and the employee category attributes at the time of creating an instance using the constructor.

Subsequently the client object can call the display method to display details of the employee name and salary. As we are dealing only with employees who belong to category Technologies, instances of the Employee class always expect the category type and hence the salary calculator to be always of the Technologies type.

As part of its implementation of the display method, the Employee class uses the salary calculation service provided by the TechnologiesSalary class.

The main application object MainSalaryApp that needs to display the salary details of employees performs the following tasks:

  • Creates an instance of the TechnologiesSalary class by passing appropriate details required for the salary calculation.
  • Creates an Employee object and configures it with the TechnologiesSalary object created above.
  • Invokes the display method on the Employee object.
  • The Employee object makes use of the services of the TechnologiesSalary object in calculating the salary of the employee it represents. In this aspect, the Employee object acts as a client to the TechnologiesSalary object.
public class MainSalaryApp {
	public static void main(String [] args) {
		TechnologiesSalary ts = new TechnologiesSalary(15000, 400);
		Employee e = new Employee ("Jhon", ts);
		e.displayDetails();
	}
}

The above design works fine as long as the need is to calculate only the salary for TechnologiesSalary employees and there is only one class of objects that provides this service.

But the fact that the Employee object expects the salary calculation service provider object to be always of the TechnologiesSalary class type affects the maintainability and results in an application design that is restrictive in terms of its adaptability.

Let’s assume that the application also needs to calculate the salary of employees who are part of HR, such as Recruitment Manager, Human Resources, and the corresponding salary calculation service is provided by objects of a different class HRSalary.

public class HRSalary {
	double basicSalary;
	double recruitAllowance;
	final static double commission = 0.03;
	public HRSalary(double basicSalary, double recruitAllowance) {
		this.basicSalary = basicSalary;
		this.recruitAllowance = recruitAllowance;
	}
	public double getTotalSalary() {
		return (basicSalary + (commission * recruitAllowance));
	}
}

The main application object MainSalaryApp will be able to create an instance of the HRSalary class but will not be able to configure the Employee object with this instance.

This is because the Employee object expects the salary calculator to be always of the TechnologiesSalary type.

As a result, the main application will not be able to reuse the existing Employee class to represent different types of employees in the below figure.

The existing Employee class implementation needs to undergo necessary modifications to accept additional salary calculator service provider types. These limitations can be addressed by using the Interface pattern resulting in a much more flexible application design.
Interface Pattern in Java
Applying the Interface pattern, the following three changes can be made to the application design.

  • The common salary calculating service provided by different objects can be abstracted out to a separate EmployeeSalaryCalculator interface.
public interface EmployeeSalaryCalculator {
	public double getTotalSalary();
}
  • Each of the TechnologiesSalary and the HRSalary classes can be designed as implementers of the EmployeeSalaryCalculator interface in the below figure.
Interface Pattern in Java
public class TechnologiesSalary implements EmployeeSalaryCalculator {
	double basicSalary;
	double overTimeSalary;
	public TechnologiesSalary(double basicSalary, double overTimeSalary) {
		this.basicSalary = basicSalary;
		this.overTimeSalary = overTimeSalary;
	}
	public double getTotalSalary() {
		return (basicSalary + overTimeSalary);
	}
}
public class HRSalary implements EmployeeSalaryCalculator {
	double basicSalary;
	double recruitAllowance;
	final static double commission = 0.03;
	public HRSalary(double basicSalary, double recruitAllowance) {
		this.basicSalary = basicSalary;
		this.recruitAllowance = recruitAllowance;
	}
	public double getTotalSalary() {
		return (basicSalary + (commission * recruitAllowance));
	}
}
  • The Employee class implementation needs to be changed to accept a salary calculator service provider of type EmployeeSalaryCalculator.
public class Employee {
	String employeeName;
	EmployeeSalaryCalculator empSalCalc;
	public Employee(String employeeName, EmployeeSalaryCalculator empSalCalc) {
		this.employeeName = employeeName;
		this.empSalCalc = empSalCalc;
	}
	public void displayDetails() {
		System.out.println("Name : " + employeeName);
		System.out.println("salary : " + empSalCalc.getTotalSalary());
	}
}
  • With these changes in place, the main application object MainSalaryApp can now create objects of different types of salary calculator classes and use them to configure different Employee objects. Because the Employee class, in the revised design, accepts objects of the EmployeeSalaryCalculator type, it can be configured with an instance of any EmployeeSalaryCalculator implementer class (or its subclass).
public class MainSalaryApp {
	public static void main(String [] args) {
		TechnologiesSalary s = new TechnologiesSalary(15000, 400);
		Employee e = new Employee ("Jhon", s);
		e.displayDetails();
		s = new HRSalary(18000, 500);
		e = new Employee("Bill", s);
		e.displayDetails();
	}
}

The below figure shows application objects association.
Interface Pattern in Java
Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *