Convert Static to Dynamic Construction -- 静态到动态构造转换

来源:互联网 发布:软件陷阱 编辑:程序博客网 时间:2024/06/04 19:54

Refactoring contributed by Gerard M. Davison

You have classes that have static compile time dependencies on classes that can only be built on a specific platform.

你有一些平台独立的静态编译时期的类

Make use of the java.lang.reflect to break the static dependency.

利用java反射打破静态依赖。

 
  import org.davison.data.jdbc.JDBCProvider;   .   .   .   DataProvider dp = new JDBCProvider();

   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());   }

Motivation

In some cases you have to provide drivers to provide different functionality depending on the situation. You might have classes that on a given platform need to access some sun.* classes to perform a specific function. For example a class to access WinHelp needs to get the window handle via the sun.awt.windows.WWindowPeer classes.

有时候你会根据情况提供不同功能的驱动程序。

There might also be the desire to only provide certian functionality to a given sub-set of users. This is the equivalent of being able to compile out code without having to alter the source or use pre-compilers.

你可能期望给予用户不同的功能子集,这就相当于不用修改源码或者预编译就能达到这种目的。

This method can be used with good effect with application frameworks where you do not know which class need to be used at compile time.

在应用程序框架中使用反射是一种的好的方式,譬如在编译期你不知道到底需要选择哪个类时。

Mechanics

  • Identify the place 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.
  • Make sure that there is a common interface and method of construction. Utilise Extract Interface or use Extract Superclass to create a suitable abstraction to work with.
  • Replace the class selection process with one that instead selects for the string name of the class in question.
  • Remove any import statements that refere the the classes being constructed.
  • Add code in to load and instantiate the class. For more parameterised contructors, make use of the java.lang.reflect package.
  • Deal with any excpetion generated.
  • You are now ready to compile and test the code. Make sure that any dependant code is also tested properly.
  • The refactoring is complete, but you should make sure that this run-time dependency is properly documented and that all developers know to instantiate the classes using the new or altered method.

Example

Start with this code:

   import org.davison.data.jdbc.JDBCProvider;   .   .   .   DataProvider dp = new JDBCProvider();

First we have to change the selection code to return a class name rather than an actual instance. We can safely remove the import statement that referes to this class.

  
 Class.forName("org.davison.data.jdbc.JDBCProvider")

We can now instantiate the class, in this simple case the constructor has no parameters, so we do not have to make use of the extended java.lang.reflect package.

     
 DataProvider dp = (DataProvider)         Class.forName("org.davison.data.jdbc.JDBCProvider").newInstance();

We now have to add the code to deal with the possible exception cases. Here I have chosen to convert them to the equivalent Java Errors in order to maintain the interface. If this is not required then simpler code is possible.

 
  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());   }

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.

原创粉丝点击