Design Patterns - ayushmathur94/DirectQuesAns_Prep GitHub Wiki

Creational Design Patterns

Creational design patterns provide solution to instantiate an object in the best possible way for specific situations. These are related to object creation.
Singleton Pattern   see here
Factory Pattern   see here
Abstract Factory Pattern
Builder Pattern
Prototype Pattern

Structural Design Patterns

Adapter Pattern
Composite Pattern
Proxy Pattern
Flyweight Pattern
Facade Pattern
Bridge Pattern
Decorator Pattern

Behavioral Design Patterns

Template Method Pattern
Mediator Pattern
Chain of Responsibility Pattern
Observer Pattern
Strategy Pattern
Command Pattern
State Pattern
Visitor Pattern
Interpreter Pattern
Iterator Pattern
Memento Pattern

Miscellaneous Design Patterns

DAO Design Pattern
Dependency Injection Pattern
MVC Pattern

Singleton Pattern

  1. Singleton Pattern restricts instantiation of class and ensures that only one instance of the class exists in the Java Virtual Machine. It seems to be a very simple design pattern but when it comes to implementation, it comes with a lot of implementation concerns.
  2. The singleton class must provide a global access point to get the instance of the class. (ie the instance should be globally accessible.)
  3. Singleton classes are used for logging, driver objects, caching and thread pool, database connections.
  4. Singleton design pattern is also used in other design patterns like Abstract Factory, Builder, Prototype, Facade etc.
  5. java.lang.Runtime and java.awt.Desktop are 2 singleton classes provided by JVM.
  6. Outer classes should be prevented to create instance of singleton class.

Implementation of Singleton class

An implementation of singleton class should have following properties:

  1. It should have only one instance : This is done by providing an instance of the class from within the class. Outer classes or subclasses should be prevented to create the instance. This is done by making the constructor private in java so that no class can access the constructor and hence cannot instantiate it.
  2. Instance should be globally accessible : Instance of singleton class should be globally accessible so that each class can use it. In Java, it is done by making the access-specifier of instance public.
    OR
    Private static variable of the same class that is the only instance of the class. And then, declare Public static method that returns the instance of the class, this is the global access point for outer world to get the instance of the singleton class.
//A singleton class should have public visibility
//so that complete application can use
public class Book {
    
  //static instance of class globally accessible
  public static Book instance = new Book();
  private Book() {
    // private constructor so that class
    //cannot be instantiated from outside
    //this class
  }
}

Another singleton example :

public class DemoSingleton implements Serializable {
    private static final long serialVersionUID = 1L;
 
    private DemoSingleton() {
        // private constructor
    }
 
    private static class DemoSingletonHolder {
        public static final DemoSingleton INSTANCE = new DemoSingleton();
    }
 
    public static DemoSingleton getInstance() {
        return DemoSingletonHolder.INSTANCE;
    }
 
    protected Object readResolve() {
        return getInstance();
    }
}

for detailed singleton pattern read this from journaldev and howtodoinjava

Factory Pattern

  • Factory Design pattern is based on Encapsulation object oriented concept. Factory method is used to create different object from factory often refereed as Item and it encapsulate the creation code. So instead of having object creation code on client side we encapsulate inside Factory method in Java.
  • The factory design pattern is used when we have a superclass with multiple sub-classes and based on input, we need to return one of the sub-class. This pattern takes out the responsibility of the instantiation of a class from the client program to the factory class. We can apply a Singleton pattern on the Factory class or make the factory method static. . One of the best examples of factory pattern in Java is BorderFactory Class of Swing API.

Example of a static factory method in JDK

Best Example of Factory method design pattern is valueOf() method which is there in String and wrapper classes like Integer and Boolean and used for type conversion i.e. from converting String to Integer or String to double in java..

Some more examples of factory method design pattern from JDK is :

valueOf() method which returns object created by factory equivalent to value of parameter passed.

getInstance() method which creates instance of Singleton class.

newInstance() method which is used to create and return new instance from factory method every time called.

getType() and newType() equivalent of getInstance() and newInstance() factory method but used when factory method resides in separate class.

What Problem is solved by Factory method Pattern in Java

  • Some time our application or framework will not know that what kind of object it has to create at run-time it knows only the interface or abstract class and as we know we can not create an object of interface or abstract class so the main problem is framework knows when it has to create but don’t know what kind of object.

  • Another problem we can face is class needs to contain objects of other classes or class hierarchies within it; this can be very easily achieved by just using the new keyword and the class constructor. The problem with this approach is that it is a very hard-coded approach to create objects as this creates a dependency between the two classes.

  • So factory pattern solves this problem very easily by model an interface for creating an object which at creation time can let its subclasses decide which class to instantiate, Factory Pattern promotes loose coupling by eliminating the need to bind application-specific classes into the code.

  • The factory methods are typically implemented as virtual methods, so this pattern is also referred to as the “Virtual Constructor”.

When to use the Factory design pattern in Java

  • Static Factory methods are common in frameworks where library code needs to create objects of types which may be sub classed by applications using the framework.
  • Some or all concrete products can be created in multiple ways, or we want to leave open the option that in the future there may be new ways to create the concrete product.
  • Factory method is used when Products don't need to know how they are created.
  • We can use factory pattern where we have to create an object of any one of sub-classes depending on the data provided.

Example of Factory

 Factory Design Pattern Super Class
Super class in factory design pattern can be an interface, abstract class or a normal java class. For our factory design pattern example, we have abstract super class with overridden toString() method for testing purpose.

package com.journaldev.design.model;

public abstract class Computer {
	
	public abstract String getRAM();
	public abstract String getHDD();
	public abstract String getCPU();
	
	@Override
	public String toString(){
		return "RAM= "+this.getRAM()+", HDD="+this.getHDD()+", CPU="+this.getCPU();
	}
}
Factory Design Pattern Sub Classes
Let’s say we have two sub-classes PC and Server with below implementation.

package com.journaldev.design.model;

public class PC extends Computer {

	private String ram;
	private String hdd;
	private String cpu;
	
	public PC(String ram, String hdd, String cpu){
		this.ram=ram;
		this.hdd=hdd;
		this.cpu=cpu;
	}
	@Override
	public String getRAM() {
		return this.ram;
	}

	@Override
	public String getHDD() {
		return this.hdd;
	}

	@Override
	public String getCPU() {
		return this.cpu;
	}

}
Notice that both the classes are extending Computer super class.

package com.journaldev.design.model;

public class Server extends Computer {

	private String ram;
	private String hdd;
	private String cpu;
	
	public Server(String ram, String hdd, String cpu){
		this.ram=ram;
		this.hdd=hdd;
		this.cpu=cpu;
	}
	@Override
	public String getRAM() {
		return this.ram;
	}

	@Override
	public String getHDD() {
		return this.hdd;
	}

	@Override
	public String getCPU() {
		return this.cpu;
	}

}
Factory Class
Now that we have super classes and sub-classes ready, we can write our factory class. Here is the basic implementation.

package com.journaldev.design.factory;

import com.journaldev.design.model.Computer;
import com.journaldev.design.model.PC;
import com.journaldev.design.model.Server;

public class ComputerFactory {

	public static Computer getComputer(String type, String ram, String hdd, String cpu){
		if("PC".equalsIgnoreCase(type)) return new PC(ram, hdd, cpu);
		else if("Server".equalsIgnoreCase(type)) return new Server(ram, hdd, cpu);
		
		return null;
	}
}

Some important points about Factory Design Pattern method are :

  • We can keep Factory class Singleton or we can keep the method that returns the subclass as static.
  • Notice that based on the input parameter, different subclass is created and returned. getComputer is the factory method.
Here is a simple test client program that uses above factory design pattern implementation.

package com.journaldev.design.test;

import com.journaldev.design.factory.ComputerFactory;
import com.journaldev.design.model.Computer;

public class TestFactory {

	public static void main(String[] args) {
		Computer pc = ComputerFactory.getComputer("pc","2 GB","500 GB","2.4 GHz");
		Computer server = ComputerFactory.getComputer("server","16 GB","1 TB","2.9 GHz");
		System.out.println("Factory PC Config::"+pc);
		System.out.println("Factory Server Config::"+server);
	}

}
Output of above program is:

Factory PC Config::RAM= 2 GB, HDD=500 GB, CPU=2.4 GHz
Factory Server Config::RAM= 16 GB, HDD=1 TB, CPU=2.9 GHz

Factory Design Pattern Advantages

  • Factory design pattern provides approach to code for interface rather than implementation.
  • Factory pattern removes the instantiation of actual implementation classes from client code. Factory pattern makes our code more robust, less coupled and easy to extend. For example, we can easily change PC class implementation because client program is unaware of this.
  • Factory pattern provides abstraction between implementation and client classes through inheritance.
⚠️ **GitHub.com Fallback** ⚠️