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.
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()); }
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.
java.lang.reflect
package.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.