Architecture & DesignA Quick Look into Common and Useful Patterns in Java

A Quick Look into Common and Useful Patterns in Java

Patterns are a pack of wisdom and lessons learned by developers who have been on the road for years to design a solution to most common problems. It would be sheer naivety to not use them, especially when we face a similar kind of situation. But, before using them, we must be aware of the tenets of their implementation. There are many such patterns to exploit in our day-to-day programming. This article picks a few of the most common among them and show how to implement in a simple manner as possible.

Overview

Martin Fowler says that the “…key part of the pattern is that they are rooted in practice. You find patterns by looking at what people do, observing things that work.” It is not always easy to find a pattern, but once found, they are quite valuable. Design patterns should be treated as good advice, an advice backed by experience and never a providence. A similar problem can be worked according to an existing pattern, but, equally possible, that there can be an exception.

Having said this, the patterns we discuss here are as follows: Singleton, Utility, Factory, and Dependency Injection. Note that we pick these not because they the most common four but only because they are just four amongst the most common. Let’s dive in. For implementation, we will use Java as the language.

Singleton Pattern

The singleton pattern evolved from the developer’s primary need to create a class which ensures that only one single instance of the class could be created at any given point of time. One may disagree to some extent that the hypothesis does not exactly adhere to the principles of thread safety. But, nonetheless, experience says we do need them in some cases. By far, this is the simplest pattern from the point of view of implementation. A naive version of this design may look like this:

package com.mano.patternsdemo;
public class Singleton {
   private static Singleton instance;
   private Singleton(){
   }
   public static Singleton getInstance(){
      if(instance == null)
         instance = new Singleton();
      return instance;
   }
}

But, there is a problem with the above simplistic code. It cannot be invoked concurrently by multiple threads because it will create multiple instances. One way to overcome this limitation is to modify the code as follows. Note that this is a non-lazy implementation.

package com.mano.patternsdemo;
public class NonLazySingleton {
   private static final NonLazySingleton instance =
      new NonLazySingleton();
   private NonLazySingleton(){
   }
   public NonLazySingleton getInstance(){
      return instance;
   }
}

If we want to make sure that resources are not wasted and create the instance lazily only when they are required, we can do so by explicit synchronization. This ensures low concurrency in a multithreaded environment.

package com.mano.patternsdemo;
public class LazySingleton {
   private static LazySingleton instance;
   private LazySingleton(){
   }
   public static synchronized LazySingleton getInstance(){
      if(instance == null)
         instance = new LazySingleton();
      return instance;
   }
}

We must understand that having many singleton patterns in a project is an absolute no-no. This means that class design is incorrect and needs rethinking. Also, a Singleton pattern makes code hard to test. It is also better to use dependency injection instead, where we may think we need a Singleton pattern.

Utility Pattern

The utility or helper pattern is useful to create a non-instantiable class. Because Java makes it easy by using final keyword, here we’ll focus on implementing without using final. The utility or helper class typically helps in keeping all sorts of unrelated methods in one place. However, this is not a good idea and is better avoided if we are to adhere to the Object-Oriented principle of reuse, cleanliness of code, and so forth, yet it seems we cannot avoid them altogether. Therefore, the utility pattern has some utility indeed!

Now, if we are to implement this pattern in code, the methods declared in the class should all be static. And as you may have guessed, if we do not use the final keyword, the only way to ensure non-instantiability is by declaring the constructor as private. Java has the advantage of the final keyword but other Object-Oriented languages such as C++ may not have that option. Therefore, declaring a constructor as private is the way to implement this pattern.

package com.mano.patternsdemo;
import java.util.List;
public class Utility {
   private Utility(){
   }
   public static Boolean compareSomething(){
      // ...
      return false;
   }
   public static void shuffleTheList(List<String> list){
      // ...
   }
   public static void doSomething(){
      // ...
   }
}

Factory Method Pattern

The factory method pattern is an extremely useful pattern and has been used extensively in the Java API design, such as ranging from factory methods to abstract factory. The idea is to use an interface for creating an object, but ultimately a subclass will decide which class to instantiate. Therefore, the goal is to defer creating objects by allowing the system to determine which class to instantiate at runtime. A simple implementation of factory method pattern is the static method that returns an object of a particular class.

package com.mano.patternsdemo;
public class Product {
   private Product(final String name){
   }
   public static Product newProduct(final String name){
      return new Product(name);
   }
}

This is a very naive implementation. We can modify the code into another variance by using interfaces or an abstract class or, in other words, use an abstract factory. This pattern leverages the code’s readability.

package com.mano.patternsdemo;
public interface Employee {
   double earnings();
}

package com.mano.patternsdemo;
public class SalariedEmployee implements Employee {
   private double basic;
   private double ta;
   private double da;
   public SalariedEmployee(double basic,
         double ta, double da){
      this.basic = basic;
      this.ta = ta;
      this.da = da;
   }
   @Override
   public double earnings() {
      return basic+(basic*ta)+(basic*da);
   }
}

package com.mano.patternsdemo;
public class HourlyEmployee implements Employee {
   private double hoursWorked;
   private double payPerHour;
   public HourlyEmployee(double hoursWorked,
         double payPerHour){
      this.hoursWorked = hoursWorked;
      this.payPerHour = payPerHour;
   }
   @Override
   public double earnings() {
      return hoursWorked*payPerHour;
   }
}

package com.mano.patternsdemo;
import java.util.Scanner;
public class EmployeeFactory {
   public enum EmployeeType {HOURLY, SALARIED};
   public Employee recruit(EmployeeType employeeType){
         Employee emp;
      Scanner scanner = newScanner(System.in);
      switch(employeeType){
         case HOURLY:
            System.out.println("Enter Hours:");
            double h = scanner.nextDouble();
            System.out.println("Enter pay per hour:");
            double p = scanner.nextDouble();
            emp = new HourlyEmployee(h,p);
            break;
         case SALARIED:
            System.out.println("Enter Basic:");
            double basic = scanner.nextDouble();
            System.out.println("Enter TA:");
            double ta = scanner.nextDouble();
            System.out.println("Enter DA:");
            double da = scanner.nextDouble();
            emp = new SalariedEmployee(basic,ta,da);
            break;
         default:
            emp = null;
            break;
      }
      return emp;
   }
}

package com.mano.patternsdemo;
public class Main {
   public static void main(String[] args) {
      EmployeeFactory employeeFactory = new EmployeeFactory();
      Employee emp1 = employeeFactory.recruit(EmployeeFactory
         .EmployeeType.HOURLY);
      Employee emp2 = employeeFactory.recruit(EmployeeFactory
         .EmployeeType.SALARIED);
      System.out.println("emp1 earns $"+emp1.earnings());
      System.out.println("emp2 earns $"+emp2.earnings());
   }
}

Note how the factory class is able to create any type of Employee object in a generic way.

Dependency Injection

The dependency injection pattern is also called inversion of control. As the name suggests, if an instance of a class depends on another class instance, the dependencies are to be met by injecting the object by means of constructors, setters, or strategies and never create the object itself. Using this pattern leverages loose coupling among classes, extensibility, and better code maintenance.

Let’s create a scenario to understand dependency injection better. The following code demonstrates a car service scenario without using a dependency pattern. The code is self-explanatory.

package com.mano.patternsdemo;
public class CarService {
   public void polishCar(){
      System.out.println("Polish Car");
   }
}

package com.mano.patternsdemo;
public class CarApp {
   private CarService carService = newCarService();
   public void processCar(){
      carService.polishCar();
   }
}

package com.mano.patternsdemo;
public class CarAppTest {
   public static void main(String[] args){
      CarApp app = new CarApp();
      app.processCar();
   }
}

The above code seems all right, but there are some limitations, for example,

The CarApp initializes CarService; that means it is tightly coupled and highly dependent. An upgradation of CarService will have undesirable ripple effect. These limit both extensibility and maintainability. Also, this type of highly dependent code is difficult to test.

Now, if we apply a dependency injection pattern to the above problem and redesign the code, observe how the tight coupling falls apart. The first objective is to create the service components.

package com.mano.patternsdemo;
public interface CarService {
   void startService();
}

package com.mano.patternsdemo;
public class PolishServiceImpl implements CarService {
   @Override
   public void startService() {
      System.out.println("Polish Service");
   }
}

package com.mano.patternsdemo;
public class WashServiceImpl implements CarService {
   @Override
   public void startService() {

      System.out.println("Wash Service");
   }
}

Next, we create service consumer classes.

package com.mano.patternsdemo;
public interface Consumer {
   void requestService();
}

package com.mano.patternsdemo;
public class CarApp implements Consumer {
   private CarService carService;
   public CarApp(CarService carService){
      this.carService = carService;
   }
   @Override
   public void requestService() {
      carService.startService();
   }
}

Next, we create dependency injector classes.

package com.mano.patternsdemo;
public interface CarServiceInjector {
   public Consumer getCustomer();
}

package com.mano.patternsdemo;
public class PolishServiceInjector implements
      CarServiceInjector {
   @Override
   public Consumer getCustomer() {
      return new CarApp(new PolishServiceImpl());
   }
}

package com.mano.patternsdemo;
public class WashServiceInjector implements
      CarServiceInjector {
   @Override
   public Consumer getCustomer() {
      return new CarApp(new WashServiceImpl());
   }
}

Finally, we can write client code to test the pattern.

package com.mano.patternsdemo;
public class CarAppTest {
   public static void main(String[] args){
      CarServiceInjector injector = null;
      Consumer consumer = null;
      injector = new PolishServiceInjector();
      consumer = injector.getCustomer();
      consumer.requestService();
      injector = new WashServiceInjector();
      consumer = injector.getCustomer();
      consumer.requestService();
   }
}

Note how the service classes are created only in the injector. We can extend the code to provide many other car services without having any effect on the existing code. This made our code flexible enough to accommodate any extension. The benefits of dependency injection pattern are as follows:

  • Reduces boilerplate code
  • Leverages separation of concerns
  • Flexible to extension
  • Easy to test

A bit of caution, though; dependency injection must not be overused because it may lead to unwanted maintenance problems. Also, because service classes are hidden sometimes, the errors that were supposed to crop up during compile time are caught only during runtime. This creates difficulty while debugging.

Conclusion

These are just quick popular four patterns used in day-to-day programming. Here, we have tried to give a quick overview of four only; there are many others. The dependency injection pattern is slightly more complicated, but by far is the best to address the issue of separation of concern. It is perfectly all right not to use any pattern when programming, but the question is why not use one if we can find a match. Experienced programmers always try to find a solution in the list of patterns and use them if possible.

Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.
Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.

Latest Posts

Related Stories