Recent Posts

Thursday, 21 April 2016

Java Design Patterns

                           Anti-patterns are certain patterns in software development that is considered a bad programming practice. God Object is example of Anti-Pattern. Here God Object means all operations are performed only one object. For example,

class GodObject {
    function PerformInitialization () {}
    function ReadFromFile () {}
    function WriteToFile () {}
    function DisplayToScreen () {}
    function PerformCalculation () {}
    function ValidateInput () {}
    // and so on... //
}
Here the GodObject performed all operations.
The basic idea behind object-oriented programming is that a big problem is separated into several smaller problems (a divide and conquer strategy) and solutions are created for each of them. Once the small problems have been solved, the big problem as a whole has been solved. Therefore an object need only know everything about itself.
The opposed patterns of Anti-patterns are Designed patterns. Design patterns are solutions to general problems that software developers faced during software development. Design patterns are considered as a good programming practice in application development or software development. For example,
class FileInputOutput {
    function ReadFromFile () {}
    function WriteToFile () {}
}
class UserInputOutput {
    function DisplayToScreen () {}
    function ValidateInput () {}
}
class Logic {
    function PerformInitialization () {}
    function PerformCalculation () {}
}
My main intention is due to the problems faced in designing the software applications. Mostly of the time what happen is we design the software we implement them and by the time we ready to deliver the product to the customer. When that time client changes the requirements and due to this change of requirement then we have to do lots of changes in the code before deliver it to the customer. Due to this we face many problems as for as the implementation goes. In order to prevent these problems we use design patterns.
Design pattern is the generic solutions to the common problems which are encountered in object oriented software design. Here very important point is changes are minimal. Patterns are language and domain independent strategies for solving common object oriented design problems
   Design patterns are mainly divided into 3 types
1.      Creational Design Patterns
2.      Structural Design Patterns
3.      Behavioral Design Patterns

1.             Creational Design Patterns:
Creational design patterns are concerned with the way of creating objects. These design patterns are used when a decision must be made at the time of instantiation of a class (i.e. creating an object of a class).
But everyone knows an object is created by using new keyword in java. For example:
                                      Student s1=new Student (); 
Hard-Coded code is not the good programming approach. Here, we are creating the instance by using the new keyword. Sometimes, the nature of the object must be changed according to the nature of the program. In such cases, we must get the help of creational design patterns to provide more general and flexible approach.
There are 6 types of creational design patterns.
1.      Factory Pattern
2.      Abstract Factory Pattern
3.      Singleton Pattern
4.      Prototype Pattern
5.      Builder Pattern
6.      Object Pool Pattern
1.            Factory Pattern
A Factory Pattern or Factory Method Pattern says that just define an interface or abstract class for creating an object but let the subclasses decide which class to instantiate. In other words, subclasses are responsible to create the instance of the class.
The Factory Method Pattern is also known as Virtual Constructor.
Factory design pattern is used when we have a super class with multiple sub-classes and based on input, we need to return one of the sub-classes. This pattern takes out the responsibility of instantiation of a class from client program to the factory class.
Super Class
Super class in factory pattern can be an interface, abstract class or a normal java class. For our example, we have super class as abstract class with overridden toString () method for testing purpose.
package com.ashok.designpatterns.factory;

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();
    }
}
Sub Classes
Let’s say we have two sub-classes PC and Server with below implementation. Notice that both the classes are extending Computer class.
Client.java
package com.ashok.designpatterns.factory;

public class Client extends Computer {
        
    private String ram;
    private String hdd;
    private String cpu;
    
    public Client(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;
    }
}
Server.java
package com.ashok.designpatterns.factory;

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.ashok.designpatterns.factory;

public class ComputerFactory {
    public static Computer getComputer(String type, String ram, String hdd, String cpu){
        if("Client".equalsIgnoreCase(type))
              return new Client(ram, hdd, cpu);
        else if("Server".equalsIgnoreCase(type))
              return new Server(ram, hdd, cpu);
      return null;
    }
}
Here is a simple test client program that uses above factory pattern implementation.
TestFactory.java
package com.ashok.designpatterns.factory;

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

Output of above program is
Factory Client Config:: RAM= 2 GB, HDD=500 GB, CPU=2.4 GHz
Factory Server Config:: RAM= 16 GB, HDD=1 TB, CPU=2.9 GHz

2.            Abstract Factory Pattern
Abstract Factory is one of the Creational pattern and almost similar to Factory Pattern except the fact that it’s more like factory of factories.
If you are familiar with factory design pattern in java, you will notice that we have a single Factory class that returns the different sub-classes based on the input provided and factory class uses if-else or switch statement to achieve this.
In Abstract Factory pattern, we get rid of if-else block and have a factory class for each sub-class and then an Abstract Factory class that will return the sub-class based on the input factory class. At first it seems confusing but once you see the implementation, it’s really easy to grasp and understand the minor difference between Factory and Abstract Factory pattern.
Super Class and Sub-Classes
Computer.java
package com.ashok.designpatterns.abstractfactory;

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();
    }
}
Client.java
package com.ashok.designpatterns.abstractfactory;

public class Client extends Computer {
        
    private String ram;
    private String hdd;
    private String cpu;
    
    public Client(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;
    }
}

Server.java
package com.ashok.designpatterns.abstractfactory;

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 Classes for Each sub-class
First of all we need to create an Abstract Factory interface or abstract class.
ComputerAbstractFactory.java
package com.ashok.designpatterns.abstractfactory;

public interface ComputerAbstractFactory {
    public Computer getComputerDetails();
}
Notice that getComputerDetails() method is returning an instance of super class Computer. Now our factory classes will implement this interface and return their respective sub-class.

ClientFactory.java
package com.ashok.designpatterns.abstractfactory;

public class ClientFactory implements ComputerAbstractFactory {
        
    private String ram;
    private String hdd;
    private String cpu;
    
    public ClientFactory(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    @Override
    public Computer getComputerDetails() {
        return new Client(ram,hdd,cpu);
    }
}

ServerFactory.java
package com.ashok.designpatterns.abstractfactory;

public class ServerFactory implements ComputerAbstractFactory {
    private String ram;
    private String hdd;
    private String cpu;
    
    public ServerFactory(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    
    @Override
    public Computer getComputerDetails() {
        return new Server(ram,hdd,cpu);
    }
}
Now we will create a consumer class that will provide the entry point for the client classes to create sub-classes.
ComputerFactory.java
package com.ashok.designpatterns.abstractfactory;

public class ComputerFactory {
    public static Computer getComputer(ComputerAbstractFactory factory){
        return factory.getComputerDetails();
    }
}
Let’s write a simple test method and see how to use the abstract factory to get the instance of sub-classes.
package com.ashok.designpatterns.abstractfactory;

public class TestAbstractFactory {
    public static void main(String[] args) {
        testAbstractFactory();
    }
    private static void testAbstractFactory() {
        Computer pc = ComputerFactory.getComputer(new ClientFactory("2 GB","500 GB","2.4 GHz"));
        Computer server = ComputerFactory.getComputer(new ServerFactory("16 GB","1 TB","2.9 GHz"));
        System.out.println("AbstractFactory Client Config:: "+pc);
        System.out.println("AbstractFactory Server Config:: "+server);
    }
}
Output is
AbstractFactory Client Config:: RAM= 2 GB, HDD=500 GB, CPU=2.4 GHz
AbstractFactory Server Config:: RAM= 16 GB, HDD=1 TB, CPU=2.9 GHz

3.            Singleton Design Pattern:
Singleton pattern restricts the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine. The singleton class must provide a global access point to get the instance of the class. Singleton pattern is used for logging, driver’s objects, caching and thread pool.
Singleton design pattern is also used in other design patterns like Abstract Factory, Builder, Prototype, Facade etc.
Java Singleton Pattern
To implement Singleton pattern, we have different approaches but all of them have following common concepts.
ü    Private constructor to restrict instantiation of the class from other classes.
ü    Private static variable of the same class that is the only instance of the class.
ü   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.
In further sections, we will learn different approaches of Singleton pattern implementation and design concerns with the implementation.
ü    Eager initialization
ü    Static block initialization
ü    Lazy Initialization
ü    Thread Safe Singleton
ü    Bill Pugh Singleton Implementation
Eager initialization
In eager initialization, the instance of Singleton Class is created at the time of class loading, this is the easiest method to create a singleton class but it has a drawback that instance is created even though client application might not be using it.
Here is the implementation of static initialization singleton class.
package com.ashok.designpatterns.singleton;

public class Eager {
    private static final Eager instance = new Eager();
    private Eager(){}
    public static Eager getInstance(){
        return instance;
    }
}
       If your singleton class is not using a lot of resources, this is the approach to use. But in most of the scenarios, Singleton classes are created for resources such as File System, Database connections etc and we should avoid the instantiation until unless client calls the getInstance method. Also this method doesn’t provide any options for exception handling.
Static block initialization
Static block initialization implementation is similar to eager initialization, except that instance of class is created in the static block that provides option for exception handling.
package com.ashok.designpatterns.singleton;

public class StaticBlockSingleton {
    private static StaticBlockSingleton instance;
    private StaticBlockSingleton(){}
    static{
        try{
            instance = new StaticBlockSingleton();
        }catch(Exception e){
            throw new RuntimeException("Exception in creating singleton instance");
        }
    }
    public static StaticBlockSingleton getInstance(){
        return instance;
    }
}
Both eager initialization and static block initialization creates the instance even before it’s being used and that is not the best practice to use.
Lazy Initialization
Lazy initialization method to implement Singleton pattern creates the instance in the global access method. Here is the sample code for creating Singleton class with this approach.
package com.ashok.designpatterns.singleton;

public class LazyInitializedSingleton {
    private static LazyInitializedSingleton instance;
    private LazyInitializedSingleton(){}
    public static LazyInitializedSingleton getInstance(){
        if(instance == null){
            instance = new LazyInitializedSingleton();
        }
        return instance;
    }
}
The above implementation works fine incase of single threaded environment but when it comes to multithreaded systems, it can cause issues if multiple threads are inside the if loop at the same time. It will destroy the singleton pattern and both threads will get the different instances of singleton class.
Thread Safe Singleton
The easier way to create a thread-safe singleton class is to make the global access method synchronized, so that only one thread can execute this method at a time. General implementation of this approach is like the below class.
package com.ashok.designpatterns.singleton;

public class ThreadSafeSingleton {
    private static ThreadSafeSingleton instance;
    private ThreadSafeSingleton(){}
    public static synchronized ThreadSafeSingleton getInstance(){
        if(instance == null){
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }
}
Above implementation works fine and provides thread-safety but it reduces the performance because of cost associated with the synchronized method, although we need it only for the first few threads who might create the separate instances (Read: Java Synchronization). To avoid this extra overhead every time, double checked locking principle is used. In this approach, the synchronized block is used inside the if condition with an additional check to ensure that only one instance of singleton class is created.
Below code snippet provides the double checked locking implementation.
public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){
    if(instance == null){
        synchronized (ThreadSafeSingleton.class) {
            if(instance == null){
                instance = new ThreadSafeSingleton();
            }
        }
    }
    return instance;
}
Bill Pugh Singleton Implementation
Prior to Java 5, java memory model had a lot of issues and above approaches used to fail in certain scenarios where too many threads try to get the instance of the Singleton class simultaneously. So Bill Pugh came up with a different approach to create the Singleton class using a inner static helper class. The Bill Pugh Singleton implementation goes like this;
package com.ashok.designpatterns.singleton;

public class BillPughSingleton {
    private BillPughSingleton(){}
    private static class SingletonHelper{
        private static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }
    public static BillPughSingleton getInstance(){
        return SingletonHelper.INSTANCE;
    }
}
       Notice the private inner static class that contains the instance of the singleton class. When the singleton class is loaded, SingletonHelper class is not loaded into memory and only when someone calls the getInstance method, this class gets loaded and creates the Singleton class instance.
This is the most widely used approach for Singleton class as it doesn’t require synchronization.

4.  Prototype Pattern:
Prototype Pattern says that cloning of an existing object instead of creating new one and can also be customized as per the requirement. Prototype pattern refers to creating duplicate object while keeping performance in mind. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object.

Shape.java
package com.ashok.designpatterns.prototype;

public abstract class Shape implements Cloneable {
   private String id;
   protected String type;

   abstract void draw();
  
   public String getType(){
      return type;
   }
  
   public String getId() {
      return id;
   }
  
   public void setId(String id) {
      this.id = id;
   }
  
   public Object clone() {
      Object clone = null;
      try {
         clone = super.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      return clone;
   }
}

Rectangle.java
package com.ashok.designpatterns.prototype;

public class Rectangle extends Shape {
   public Rectangle(){
     type = "Rectangle";
   }
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

Square.java
package com.ashok.designpatterns.prototype;

public class Square extends Shape {
   public Square(){
     type = "Square";
   }

   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

Circle.java
package com.ashok.designpatterns.prototype;

public class Circle extends Shape {
   public Circle(){
     type = "Circle";
   }

   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

ShapeCache.java
package com.ashok.designpatterns.prototype;

import java.util.Hashtable;
public class ShapeCache {
   private static Hashtable<String, Shape> shapeMap  = new Hashtable<String, Shape>();

   public static Shape getShape(String shapeId) {
      Shape cachedShape = shapeMap.get(shapeId);
      return (Shape) cachedShape.clone();
   }
   public static void loadCache() {
      Circle circle = new Circle();
      circle.setId("1");
      shapeMap.put(circle.getId(),circle);

      Square square = new Square();
      square.setId("2");
      shapeMap.put(square.getId(),square);

      Rectangle rectangle = new Rectangle();
      rectangle.setId("3");
      shapeMap.put(rectangle.getId(), rectangle);
     }
}

PrototypePatternDemo.java
package com.ashok.designpatterns.prototype;

public class PrototypePatternDemo {
   public static void main(String[] args) {
      ShapeCache.loadCache();
      Shape clonedShape = (Shape) ShapeCache.getShape("1");
      System.out.println("Shape : " + clonedShape.getType());       
      Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
      System.out.println("Shape : " + clonedShape2.getType());            
      Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
      System.out.println("Shape : " + clonedShape3.getType());            
   }
}
Output:
Shape: Circle
Shape: Square
Shape: Rectangle

No comments:

Post a Comment