Convert Dynamic to Static Construction

You have code that loads other classes dynamically. This can introduce a un-warranted overhead and can produce code that is more fragile.

Replace the dynamic class loading with static code.



   try
   {
      DataProvider dp = (DataProvider)
         Class.forName("org.davison.data.jdbc.JDBCProvider").newInstance();
   }
   catch (IllegalAccessException iae)
   {
      // Convert exception to error to preseve the interface.
      //
      
      throw new IllegalAccessError(iae.getMessage());
   }
   catch (InstantiationException ie)
   {
      // Convert exception to error to preseve the interface.
      //
      
      throw new InstantiationError(ie.getMessage());      
   }
   catch (ClassNotFoundException cnfe)
   {
      // Convert exception to error to preseve the interface.
      //

      throw new NoClassDefFoundError(cnfe.getMessage());
   }


image/svg+xml


   import org.davison.data.jdbc.JDBCProvider;

   .
   .
   .
   
   DataProvider dp = new JDBCProvider();

Motivation

In some cases code is written with dynamic dependencies between parts of the code by utilising Java ability to load and instantiate arbitrary classes. If not properly managed, this can cause run-time errors. This design can impart a performance penalty because of the extra levels of indirection. It can also prevent the compiler from detecting certain types of error.

Mechanics

  • Identify the places where the different classes are instantiated. If there is more than one place, you might like to make use of code consolidation refactorings to create one entry point.
  • You can remove the code that instantiates the classes using java.lang.reflect. We will instead call the constructors directly.
  • Replace the code that selects the name of the class with simpler code that simply returns a new instance.
  • Add in import statements as required for the classes being constructed.
  • You are now ready to compile and test the code. Make sure that any dependant code is also tested properly.
  • The refactoring is complete.

Example

Start with this code:


   try
   {
      DataProvider dp = (DataProvider)
         Class.forName("org.davison.data.jdbc.JDBCProvider").newInstance();
   }
   catch (IllegalAccessException iae)
   {
      // Convert exception to error to preseve the interface.
      //
      
      throw new IllegalAccessError(iae.getMessage());
   }
   catch (InstantiationException ie)
   {
      // Convert exception to error to preseve the interface.
      //
      
      throw new InstantiationError(ie.getMessage());      
   }
   catch (ClassNotFoundException cnfe)
   {
      // Convert exception to error to preseve the interface.
      //

      throw new NoClassDefFoundError(cnfe.getMessage());
   }


We can remove the error handling code and simply instantiate the class.



   DataProvider dp = new JDBCProvider();
   

The final step is to add in the correct import statement


   import org.davison.data.jdbc.JDBCProvider;

   .
   .
   .
   
   DataProvider dp = new JDBCProvider();
   

Compile and test at this point as we have code that is complete.

When this is finished and all dependent classes are re-tested, the refactoring is complete.

Another way of creating compile time dependencies is to use class literals. This would still have the overhead of dynamic instantiation. I would not recommend this unless you have good reason; but here is the code converted to use this method.



   try
   {
      DataProvider dp = (DataProvider)
         org.davison.data.jdbc.JDBCProvider.class.newInstance();
   }
   catch (IllegalAccessException iae)
   {
      // Convert exception to error to preseve the interface.
      //
      
      throw new IllegalAccessError(iae.getMessage());
   }
   catch (InstantiationException ie)
   {
      // Convert exception to error to preseve the interface.
      //
      
      throw new InstantiationError(ie.getMessage());      
   }
   catch (ClassNotFoundException cnfe)
   {
      // Convert exception to error to preseve the interface.
      //

      throw new NoClassDefFoundError(cnfe.getMessage());
   }