In this tutorial we will discuss Design Patterns in Java. We will study Singleton, Factory and Builder patterns with examples and advantages:
When we solve a specific problem, we can solve it more efficiently if we have some best practices already defined. This indeed will help us to follow these best practices and develop more reliable solutions.
These best practices that can be used to develop well-proved solutions for solving problems are called “Design Patterns”. Experienced OOP software developers use design patterns to solve specific tasks.
=> Take A Look At The Java Beginners Guide Here
Table of Contents:
Design Patterns In Java
Design patterns were first invented by Christopher Alexander in 1977. But later on four developers namely Erich Gamma, Richard Helm, John Vlissides, and Ralph Johnson wrote a book titled, “Gang of Four-Design patterns, elements of reusable object-oriented software” in the year 1995.
From then all the design patterns came to be known as “Gang of Four Design Patterns”.
Design patterns are independent of any programming languages as they are used to solve common object-oriented design problems and are not just limited to a specific programming language. So basically it’s an idea and not an implementation.
Thus using design patterns we can develop programs that are more efficient, flexible, maintainable, and reusable.
Advantages Of Design Patterns
Enlisted below are some of the advantages of using design patterns in our applications:
- Design patterns are reusable and can be used by multiple projects.
- We can define system architecture using design patterns.
- Design patterns provide transparency to the application design.
- Design patterns are already well-tested and proved so that we can use them without any worries.
- Design patterns allow us to build better systems and also provide clarity on system architecture.
When To Use Design Patterns
So when should we exactly use the design patterns?
We usually use a design pattern during the initial Analysis and requirement phase of SDLC (Software Development Life Cycle). When used during the analysis and requirement phase of SDLC, it provides more information to this phase. Java internally supports design patterns.
Design patterns in Java are categorized as follows:
As far as this tutorial is concerned, we are interested only in creational design patterns.
Creational design patterns are further classified as follows:
In this tutorial, we will discuss the following design patterns:
- Singleton design pattern
- Factory design pattern
- Builder design pattern
Let’s start with a singleton design pattern in Java.
Recommended reading =>> Design Patterns for Flask based Apps
Singleton Pattern In Java
A singleton pattern is a type of creational pattern in Java. Singleton pattern is a design pattern in which only one instance of a class is present in the Java virtual machine. A singleton class (implementing singleton pattern) has to provide a global access point to get the instance of the class.
In other words, a singleton pattern restricts the instantiation of a class. Singleton pattern is used in the implementation of the logger for applications. It is also used in thread pool implementation or cache.
The Java classes, java.awt.Desktop and java.lang.runtime also use a singleton pattern.
Advantages
- As only one instance of the singleton class is used, we save memory.
- Also, ensures reusability as the same singleton object is used again and again.
Now let’s move onto the implementation of the singleton pattern.
Implementation Of The Singleton Pattern
As already mentioned, a singleton design pattern restricts the class with only one instance and this instance is given a global point of access. These were all classes that refer to the same object again and again.
The following UML diagram explains the Singleton pattern.
As the above UML diagram shows, a singleton class has a single instance defined and we access it by the getInstance () method. So a singleton factory that is responsible for creating objects makes use of the getInstance method to return the same object (which is there in the class) again and again.
So how do we implement the Singleton Pattern in a Program?
We create a singleton class and have its constructor as “private” so that the class cannot be instantiated. Then we create a private instance of this singleton class inside the class itself. Then we have a special public method getInstance () that returns a singleton object to the outside world.
This implementation of this singleton class as explained above is shown in the Java program below.
class SingletonObject { //create an object of SingletonObject private static SingletonObject instance = new SingletonObject(); //private constructor so that we cannot instantiate the class private SingletonObject(){} //returns the only available object public static SingletonObject getInstance(){ return instance; } public void printMessage(){ System.out.println("Hello from Singleton object!!!"); } } public class Main { public static void main(String[] args) { //illegal statement because constructor is private //Compile Time Error: The constructor SingletonObject() is not visible //SingletonObject object = new SingletonObject(); //call getInstance to retrieve the object available from the class SingletonObject object = SingletonObject.getInstance(); //show the message object.printMessage(); } }
Output:
Now if we check the main method, note that if we try to create an object of the singleton class using a new operator, the compiler will give a compilation error (see the commented code in the main method). We obtain the object of the singleton class using the getInstance () method and then we can use it as usual to access methods.
Factory Pattern In Java
The factory pattern is also called “Factory Method pattern” or “Virtual Constructor” in Java. In this pattern, we create an interface or an abstract class with method declarations, and then the concrete classes or subclasses implementing this interface or inheriting the class are responsible for creating instances of the class.
Advantages
- The factory pattern is a type of creational pattern and is the most commonly used pattern in Java.
- By using a factory pattern we ensure that the actual creation logic is not exposed to the outside world.
So if a class implementing a factory pattern has a method to calculate the rate of interest, then the concrete classes will implement this class and also implement the method to calculate the rate of interest.
Then there will be another class that is a factory class that will access these concrete class instances so that we are not aware of how the logic to calculate the rate of interest is implemented. We only call the method and get the output.
So when exactly can we use the Factory Method pattern?
When the parent classes decide to delegate the creation of instances to their subclasses, then we can go for a factory pattern (This is explained above). We can also use the factory method when the class doesn’t know what subclasses are to be created.
Now let’s see the implementation of the factory method pattern.
Implementation Of The Factory Pattern
As an example let’s implement a generic shape interface. We can derive various concrete classes from this interface like the circle, rectangle, etc. Then we will have a shapeFactory class that will access the concrete class objects. The UML for this pattern is shown below.
As already explained this is the UML diagram for factory pattern. Now we will implement a Java program demonstrating the factory pattern.
//Geometric_shape interface interface Geometric_shape { void draw_shape(); } //Geometric shape classes implementing Geometric_shape interface class Rectangle implements Geometric_shape { @Override public void draw_shape() { System.out.println("Rectangle class::draw_shape() method."); } } class Square implements Geometric_shape { @Override public void draw_shape() { System.out.println("Square class::draw_shape() method."); } } class Circle implements Geometric_shape { @Override public void draw_shape() { System.out.println("Circle class::draw_shape() method."); } } //Factory class for Geometric_shape class ShapeFactory { //shapeObject method gets particular shapeType (circle, Square or Rectangle) public Geometric_shape shapeObject(String shapeType){ if(shapeType == null){ return null; } //retrieve Circle object if(shapeType.equalsIgnoreCase("Circle")){ return new Circle(); //retrieve Rectangle object } else if(shapeType.equalsIgnoreCase("Rectangle")){ return new Rectangle(); ////retrieve Square object } else if(shapeType.equalsIgnoreCase("Square")){ return new Square(); } return null; } } public class Main { public static void main(String[] args) { //Create a ShapeFactory object to get different geometric shapes ShapeFactory shapeFactory = new ShapeFactory(); //circle Geometric_shape shape_Circle = shapeFactory.shapeObject("CIRCLE"); //draw method of Circle shape_Circle.draw_shape(); //Rectangle Geometric_shape shape_Rectangle = shapeFactory.shapeObject("RECTANGLE"); //draw method of Rectangle shape_Rectangle.draw_shape(); //Square Geometric_shape shape_Square = shapeFactory.shapeObject("SQUARE"); //draw method of square shape_Square.draw_shape(); } }
Output:
Builder Pattern In Java
In the Builder pattern, we use a step-by-step approach to build a complex object using small, and simple objects.
So whenever we encounter an object that cannot be created in a single step, we go for a builder pattern.
Advantages
- Using the Builder pattern, we can separate the construction and representation of an object.
- We also can change the internal representation of the object.
- We can build complex designs like an entire delivery system using the builder pattern.
A practical example of a Builder pattern is the food ordering system which involved complex steps of collecting food items that are ordered, then packaging, billing, building order, and then shipping it.
In this tutorial, we will implement an example of a tablet ordering system using the Builder pattern.
Implementation Of Builder Pattern
The general UML diagram for the Builder pattern is given below.
The above diagram shows the Builder pattern UML diagram. As shown in the above diagram, we have four components in the Builder pattern.
- Product: This represents the complex object to be built.
- Builder Abstract class: An abstract class containing prototypes of all the functionality required to build a complex object.
- ConcreteBuilder Class: This is a concrete class that inherits from the Builder class and creates a particular complex object. We can have as many ConcreteBuilder classes as we need.
- Director class: This class controls the algorithms that generate the final product.
The following programming example shows the demonstration of a Builder pattern using a tablet ordering building system.
import java.util.ArrayList; import java.util.List; //Packing interface for tablets interface Packing { public String pack(); public int price(); } //Tablet class - abstract abstract class Tablet implements Packing{ public abstract String pack(); } //company - extends Tablet abstract class Company extends Tablet{ public abstract int price(); } //Lenovo tablet class Lenovo extends Company{ @Override public int price(){ return 541; } @Override public String pack(){ return "Lenovo Yoga"; } } //Micromax tablet class MicroMax extends Company { @Override public int price(){ return 338; } @Override public String pack(){ return "MicroMax"; } } //Tablet type class TabType { private List<Packing> items=new ArrayList<Packing>(); //add items public void addItem(Packing packs) { items.add(packs); } //retrieve cost public void getCost(){ for (Packing packs : items) { packs.price(); } } //show all items public void showItems(){ for (Packing packing : items){ System.out.print("Tablet name : "+packing.pack()); System.out.println(", Price(in U.S.Dollars) : "+packing.price()); } } } //builder class for tablets order class TabBuilder { public TabType buildLenovoTab(){ TabType lenovo =new TabType(); lenovo.addItem(new Lenovo()); return lenovo; } public TabType buildMicroMaxTab(){ TabType mmx=new TabType(); mmx.addItem(new MicroMax()); return mmx; } } public class Main{ public static void main(String args[]){ //build the tablets order and display the order TabBuilder tabBuilder=new TabBuilder(); TabType tabtype1=tabBuilder.buildLenovoTab(); tabtype1.showItems(); TabType tabtype2=tabBuilder.buildMicroMaxTab(); tabtype2.showItems(); } }
Output:
In the above example, we have built the complete tablet ordering system for two tablet brands i.e. Lenovo and Micromax. These are the concreteBuilder classes that inherit from the abstract class company. Then we have a TabBuilder class that builds the orders for both the tablet classes.
Frequently Asked Questions
Q #1) What are Design Patterns in Java? What are the types of design patterns in Java?
Answer: Design patterns are the best practices that can be used to develop well-tested solutions.
Java has three types of design patterns:
- Creational design pattern: Factory pattern, Abstract Factory pattern, Singleton pattern, Builder pattern, and prototype pattern are examples of creational design patterns. These are mainly involved with the creation of objects.
- Structural design pattern: They are mostly used for creating a class structure. Adapter, Bridge, and composite pattern are popular structural design patterns.
- Behavioral design pattern: These provide better interaction between the objects along with the flexibility for easily extending the implementation. Observer patterns, strategy pattern, etc. are some examples of the behavioral patterns.
Q #2) Why are Design Patterns used?
Answer: Design patterns provide us a proven and tested solution model that we can use to solve complex problems. Design patterns allow us to build cohesive modules with loose coupling. Design patterns also make interaction between designers more efficient and effective.
Q #3) What are the examples of Patterns?
Answer: Examples of natural patterns are visible regularities found in nature. Natural patterns like symmetries, trees, waves, foams, stripes, cracks, etc. are some examples of natural patterns.
Q #4) Is MVC a Design Pattern?
Answer: Yes, it is a kind of design pattern using which we can build an application consisting of the data model, presentation, or view layer and controller. We can classify it more as an architectural pattern.
Conclusion
This completes our discussion on design patterns in Java. Although Java supports three types of design patterns viz. Creational, Structural, and Behavioral patterns, we are more interested in the creational design pattern.
As per the scope of this tutorial, we have discussed three examples of creational design patterns namely, singleton pattern, factory pattern, and builder pattern.
Singleton pattern is the simplest design pattern and the factory method is supposed to be a common design pattern that is widely used. The builder pattern is used to construct complex objects and is mostly used in developing complex applications.
=> Check Out The Perfect Java Training Guide Here.