Move Class

You have a class that is in a package that contains other classes that it is not related to in function.

Move the class to a more relevant package. Or create a new package if required for future use.


class org.davison.ui.TextThing
class org.davison.ui.TextProcessor
class org.davison.log.Logger

depends on 

class org.davison.ui.StringUtil

image/svg+xml


class org.davison.ui.TextThing
class org.davison.ui.TextProcessor
class org.davison.log.Logger

depends on

class org.davison.util.StringUtil

Motivation

Classes are often created in a packages close to where they are being used, this can make sense until the class starts to be re-used by other parts of the product. The package in question might also have just become too big. (I have a preference that my packages never have more than about 10 classes)

It is often better to move to this class to a package more related to it in form or function. This can help remove complex package level dependencies and make it easier for developers to find and re-use classes.

If there are many dependencies for the class within its own package, then Extract Class could be used first to split out the relevant parts.

Another example where is this used often is to move String resource objects into sub a res package to simplify localization compilation.

Mechanics

  • Move the class to its new folder on the source tree.
  • Remove any class files generated from compiling this class at its old location.
  • Alter the package statement in the source file to reflect the new package.
  • Create import statements for any dependant classes in original package.
  • Compile and test the class in its new package, updating and moving unit tests as required using this same method.
  • Alter, and create in some cases, the import statements on any dependee class. Note this is easier if wide imports are not used.
  • Check for classes that might dynamically instantiate this class using java.lang.reflect or the ResourceBundle api. Also look for code that might reference this class as an absolute class literal.
  • Compile and test all code that was dependant on the original class.
  • Consider applying Extract Package when you have many classes of different functionality in a given package.

Example

Lets look at the header of StringUtil


package org.davison.ui

// imports

public class StringUtil

...

We move the file and change the package header.


package org.davison.util

// imports

public class StringUtil

...

I can now compile and unit test this class in its new package. If the unit tests are in a different package class file then they might need to be updated.

I can now go on to update the dependant classes. For example here I update the import statements, I have not used a wide imports in this example but the principle is the same.



package org.davison.log

// imports

import org.davison.ui.StringUtils;

// more imports

public class Logger

...

This becomes:


package org.davison.log

// imports

import org.davison.util.StringUtils;

// more imports

public class Logger

...

Again compile and test this new class with the imports.

As mentioned before there are other ways of dynamically loading classes. You may have to look for class literals and strings constants containing the fully qualified name of the old class.



// A class literal

public Class m_utils = org.davison.ui.StringUtils.class;

// A dynamically loaded class

public Class m_utils = Class.forName("org.davison.ui.StringUtils");

// A loaded resource bundle

public ResourceBundle m_bundle = ResourceBundle.getBundle(
   "org.davison.ui.StringUtils");


These can be fixed using simple find and replaces, these dynamic examples will also be found using the units tests for all classes.



// A class literal, this will be found by the compiler.

public Class m_utils = org.davison.util.StringUtils.class;

// A dynamically loaded class

public Class m_utils = Class.forName("org.davison.util.StringUtils");

// A loaded resource bundle

public ResourceBundle m_bundle = ResourceBundle.getBundle(
   "org.davison.util.StringUtils");


One all static and dynamic cases have been dealt with, and all unit tests have run. The refactoring is complete.