Showing posts with label Design pattern. Show all posts

Introduction

Abstract Factory design pattern comes under creational design pattern. It is also called factory of factories. It provides a way to encapsulate a group of factories which have common behaviour or theme without specifying which underlying concrete objects are created. It is one level of abstraction higher than Factory pattern. In Factory Design Pattern, we get instance of one of several sub-classes whereas in Abstract Factory Design pattern we get instance of one of several factories which then get instance of one of several sub-classes.

Implementation

We are going to create Factories for Import and Export. We have one producer class ImportExportFactoryProducer which produces the factory based on type i,e. if instance of ImportFactory is required or ExportStrategy is required which then initialize the concrete classes for ImportStrategy or ExportStrategy.

1. AbstractFactoryTest class

package com.gauravbytes.test;

import com.gauravbytes._import.ImportStrategy;import com.gauravbytes.export.ExportStrategy;import com.gauravbytes.importexport.factory.AbstractImportExportFactory;import com.gauravbytes.importexport.factory.ImportExportFactoryProducer;import com.gauravbytes.importexport.factory.ImportExportFactoryTypes;

/**
 * @author Gaurav Rai Mazra
 */
public class AbstractFactoryTest {

 public static void main(String[] args) {
  int factoryType = ImportExportFactoryTypes.TYPE_EXPORT;
  int strategyType = AbstractImportExportFactory.TYPE_EXCEL;
  
  AbstractImportExportFactory ioFactory = ImportExportFactoryProducer.getInstance().getFactory(factoryType);
  
  switch (factoryType) {
   case ImportExportFactoryTypes.TYPE_EXPORT :
    ExportStrategy exportStrategy = ioFactory.getExportStrategy(strategyType);
    exportStrategy.export();
    break;
    
   case ImportExportFactoryTypes.TYPE_IMPORT : 
    ImportStrategy importStrategy = ioFactory.getImportStrategy(strategyType);
    importStrategy.importFile();
    break;
   default:
    break;
  }
 }
}

2. FactoryProducer class, In our case ImportExportFactoryProducer

package com.gauravbytes.importexport.factory;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * This is Factory producer class
 * @author Gaurav Rai Mazra
 *
 */
public class ImportExportFactoryProducer {
 private static ImportExportFactoryProducer factoryInstance = null;
 private static final Lock lock = new ReentrantLock();
 private ImportExportFactoryProducer() {
  
 }
 
 public static ImportExportFactoryProducer getInstance() {
  try {
   lock.lock();
   if (factoryInstance == null)
    factoryInstance = new ImportExportFactoryProducer();
  }
  finally {
   lock.unlock();
  }
  return factoryInstance;
 }
 
 
 public AbstractImportExportFactory getFactory(int factoryType) {
  AbstractImportExportFactory factory = null;
  switch (factoryType) {
   case ImportExportFactoryTypes.TYPE_IMPORT:
    factory = ImportFactory.getInstance(lock);
    break;
    
   case ImportExportFactoryTypes.TYPE_EXPORT:
    factory = ExportFactory.getInstance(lock);
    break;
 
   default:
    break;
  }
  return factory;
 }
}

3. Factory of Factory  and the concrete factories . In our case AbstractImportExportFactory, ImportFactory and ExportFactory.

package com.gauravbytes.importexport.factory;

import com.gauravbytes._import.ImportStrategy;import com.gauravbytes.export.ExportStrategy;

/**
 * This is factory of factory gives the instance for particular strategy
 * @author Gaurav Rai Mazra
 *
 */
public abstract class AbstractImportExportFactory {
 public static final int TYPE_EXCEL = 1;
 public static final int TYPE_PDF = 2;
 public static final int TYPE_PLAIN = 3;
 
 
 public abstract ImportStrategy getImportStrategy(int strategyType);
 public abstract ExportStrategy getExportStrategy(int strategyType);
}
package com.gauravbytes.importexport.factory;

/**
 * @author Gaurav Rai Mazra
 */
public abstract class ImportExportFactoryTypes {
 public static final int TYPE_IMPORT = 1;
 public static final int TYPE_EXPORT = 2;
}
package com.gauravbytes.importexport.factory;

import com.gauravbytes._import.ExcelImport;import com.gauravbytes._import.ImportStrategy;import com.gauravbytes._import.PdfImport;import com.gauravbytes._import.PlainTextImport;import com.gauravbytes.export.ExportStrategy;
import java.util.concurrent.locks.Lock;

/**
 * Import factory
 * @author Gaurav Rai Mazra
 */
public class ImportFactory extends AbstractImportExportFactory {

 private static ImportFactory importFactory = null;
 private ImportFactory() {
  
 }
 
 public static ImportFactory getInstance(Lock lock) {
  try {
   lock.lock();
   if (importFactory == null)
    importFactory = new ImportFactory();
  }
  finally {
   lock.unlock();
  }
  return importFactory;
 }
 
 @Override
 public ImportStrategy getImportStrategy(int strategyType) {
  ImportStrategy importStrategy = null;
  switch (strategyType) {
   case TYPE_EXCEL:
    importStrategy = new ExcelImport();
    break;
   
   case TYPE_PDF:
    importStrategy = new PdfImport();
    break;
    
   case TYPE_PLAIN:
    importStrategy = new PlainTextImport(); 
    break;
 
   default:
    break;
  }
  return importStrategy;
 }

 @Override
 public ExportStrategy getExportStrategy(int strategyType) {
  return null;
 }

}
package com.gauravbytes.importexport.factory;

import com.gauravbytes._import.ImportStrategy;import com.gauravbytes.export.ExcelExport;import com.gauravbytes.export.ExportStrategy;import com.gauravbytes.export.PdfExport;import com.gauravbytes.export.PlainTextExport;
import java.util.concurrent.locks.Lock;


/**
 * Factory to get proper strategy object based on its type
 * @author Gaurav Rai Mazra
 */
public class ExportFactory extends AbstractImportExportFactory {

 private static ExportFactory exportFactory = null;
 
 private ExportFactory() {
  
 }
 
 public static ExportFactory getInstance(Lock lock) {
  try {
   lock.lock();
   if (exportFactory == null)
    exportFactory = new ExportFactory();
  }
  finally {
   lock.unlock();
  }
  return exportFactory;
 }
 
 @Override
 public ImportStrategy getImportStrategy(int strategyType) {
  return null;
 }

 @Override
 public ExportStrategy getExportStrategy(int strategyType) {
  ExportStrategy strategy = null;
  switch (strategyType) {
   case TYPE_EXCEL:
    strategy = new ExcelExport();
    break;

   case TYPE_PDF:
    strategy = new PdfExport();
    break;

   case TYPE_PLAIN:
    strategy = new PlainTextExport();
    break;

   default:
    break;
  }
  return strategy;
 }
}

4. ImportStrategy and their concrete classes

package com.gauravbytes._import;

/**
 * @author Gaurav Rai Mazra
 */
public interface ImportStrategy {
 public void importFile();
}
package com.gauravbytes._import;

/**
 * @author Gaurav Rai Mazra
 */
public class ExcelImport implements ImportStrategy {

 @Override
 public void importFile() {
  // Logic to import excel file goes here
 }

}
package com.gauravbytes._import;

/**
 * @author Gaurav Rai Mazra
 */
public class PdfImport implements ImportStrategy {

 @Override
 public void importFile() {
  //Logic to import pdf goes here
 }

}
package com.gauravbytes._import;

/**
 * @author Gaurav Rai Mazra
 */
public class PlainTextImport implements ImportStrategy {
 @Override
 public void importFile() {
  //Logic to import plain text file
 }
}

5. ExportStrategy with their concrete classes

package com.gauravbytes.export;

/**
 * @author Gaurav Rai Mazra
 */
public interface ExportStrategy {
 public void export();
}
package com.gauravbytes.export;

/**
 * @author Gaurav Rai Mazra
 */
public class ExcelExport implements ExportStrategy {

 @Override
 public void export() {
  // Excel export code goes here
 }

}
package com.gauravbytes.export;

/**
 * @author Gaurav Rai Mazra
 */
public class PlainTextExport implements ExportStrategy {
 @Override
 public void export() {
  // Plain text export goes here
 }
}
package com.gauravbytes.export;

/**
 * @author Gaurav Rai Mazra
 */
public class PdfExport implements ExportStrategy {

 @Override
 public void export() {
  //Pdf export Goes here
 }

}

What is Strategy Design pattern?

Strategy design pattern comes under behavioural design pattern. It is also known as Policy pattern. It defines a family of algorithms and encapsulates each algorithm. The algorithm can be interchanged without changing much code. It bury algorithm implementation details in derived classes. It favours composition over inheritance.

Derived classes could be implemented using the template design pattern (We will discuss about it later on).

Implementation

We will take example of Compressing files. For compressing files, we can either use zip or can use rar compression strategy. In Strategy Design pattern, we will have following classes and interface.

1. We will define our Strategy to compress files.

package com.gauravbytes.strategy.client;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;

public interface CompressionStrategy {
     //Compression algorithm
     public void compressFiles(List<File> files) throws FileNotFoundException;
}

2. We will now create two concrete classes which define the compression Strategy. i.e. ZIP and RAR strategy to compress files.

package com.gauravbytes.strategy.client;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
public class ZipCompressionStrategy implements CompressionStrategy {
   @Override
    public void compressFiles(List<File> files) throws FileNotFoundException {
     //using ZIP approach

    }
}

package com.gauravbytes.strategy.client;
import java.io.File;
import java.util.List;
public class RarCompressionStrategy implements CompressionStrategy {
     @Override
     public void compressFiles(List<File> files) {
             //using RAR approach
    }
}

3. We need on context/utility which will actually interact with the actual strategies to get work done.

package com.gauravbytes.strategy.client;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;

public class CompressionUtility {
     private CompressionStrategy strategy;
     
     //this can be set at runtime by the application preferences
     public void setCompressionStrategy(CompressionStrategy strategy) {
             this.strategy = strategy;
    } 

    public void createArchive(List<File> files) throws Exception, FileNotFoundException {
         if (files == null)
             throw new RuntimeException(new NullPointerException("ListFile is null"));

         strategy.compressFiles(files);
    }
}

4. Now, we require client which will use the context/utility and generate the compressed files
package strategy.client;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Client {
    public static void main(String[] args) {
          List<File> files = new ArrayList<File>();
          CompressionUtility fileCompressor = new CompressionUtility();
          boolean isZipTypeCompression = false;
          //we could assume context is already set by preferences
         CompressionStrategy strategy = isZipTypeCompression ? new ZipCompressionStrategy() : new RarCompressionStrategy();

        fileCompressor.setCompressionStrategy(strategy);
       //get a list of files
       try {
         fileCompressor.createArchive(files); 
       } 
       catch (Exception e) { }
   }
}

Important note

I would like to explain how composition is favoured in Strategy design pattern. In class CompressionUtility, we have reference to CompressionStrategy interface and in context/utility class, there is one setter method for compression strategy i.e. you can change the alogorithm at run-time in context class if required.

This post is about Singleton Design pattern in java. All of us may know, there are different way to create singleton instance of class. I am going to write one of them. How we can make singleton object. But before going further, we will first learn what singleton is?

What is Singleton?

Singleton is design pattern which comes under creational pattern. Sometimes, it is important to have one instance of class or we can say, it provide single global entry point to the object.

How we can create Singleton?

There are many ways to create Singleton. Each one of them have their pros and cons. Today, we will discuss how to create Singleton with lazy Initialization with SingletonInstanceHolder class.

package singleton;

public class Singleton {
 
 private static class SingletonInstanceHolder {
  private static int MODCOUNT = 0;
  private static Singleton INSTANCE = null;
  
  private static final Object MUTEX = new Object();
  static
  {
   if (INSTANCE == null)
   { 
    synchronized (MUTEX) {
     if (INSTANCE == null)
      try {
       INSTANCE = new Singleton();
      } catch (IllegalAccessException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }
    }
   }
  }
 }
 
 private Singleton() throws IllegalAccessException {
  if(SingletonInstanceHolder.MODCOUNT > 0)
   throw new IllegalAccessException("Multiple instance"); 

  SingletonInstanceHolder.MODCOUNT++;
 }
 
 public static final Singleton getInstance() {
  return SingletonInstanceHolder.INSTANCE;
 }
 
 // Avoiding duplicate objects with serialization
 public Object readResolve() {
  return getInstance();
 }
}

The above mentioned class Singleton. This class can't have multiple objects even using reflection api because our code will throw exception if more than one instance is created with reflection.

package singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SingletonTest {
  public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, InstantiationException,
     IllegalAccessException, IllegalArgumentException,InvocationTargetException {
 
  Class demo = Class.forName("singleton.Singleton");
  Constructor con = demo.getDeclaredConstructor();
  con.setAccessible(true);
  
  for(int i=0; i<10; i++) {
   Singleton s1 = (Singleton) con.newInstance();
   System.out.println(s1.toString());
  }
 }
}

The above code when run will throw following exception. It shows that the Singleton class's object will be created only once and then it will throw exception if tried to called constructor multiple times even with reflection.

Exception in thread "main" java.lang.reflect.InvocationTargetException
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
 at singleton.SingletonTest.main(SingletonTest.java:15)
Caused by: java.lang.IllegalAccessException: Multiple instance
 at singleton.Singleton.<init>(Singleton.java:29)
 ... 5 more