Sunday, March 18, 2012

Eclipse Usability: Refactoring Abstract classes

By Ahmed Orabi

Introduction:
Eclipse development tool provides handy solutions to refactor the source code while working on the development. Tasks include renaming, moving different methods between different classes, and even plugins. There is also an ability to pull up methods to their parent classes. However, this refactoring does not work well for the cases of abstract classes, especially when you are working on a large project. This feature works best with quick uses. This is to be illustrated in the next lines.


Development Requirements:
You must have eclipse development environment. You may download it from eclipse website:

Scenario:
Select file/new. Select a new plugin project.


Set org.usability.tests.refactoring for your plugin


Create a new package. Name it org.usability.tests.refactoring.employees.

Under this package, create the following classes: AbstractEmploye, AbstractManager, and TechnicalManager

















And this will be the contents for each class:

AbstractEmployee:
public abstract class AbstractEmploye {

       public abstract int[] getResponsibilities();
      
       public String getName(){
              return "";
       }
      
       public int getDepartment(){
              return DepartmentConstants.DEFUALT_DEPARTMENT;
       }
      
       public interface DepartmentConstants {
              public int DEFUALT_DEPARTMENT= 0;
              public int ARCHIVE_DEPARTMENT= 1;
              public int TECHNICAL_DEPARTMENT= 2;
              public int MAINTENANCE_DEPARTMENT= 3;
              public int MANAGEMENT_DEPARTMENT= 3;
       }
      
       public interface BranchesConstants {
              public int MAIN_BRANCH= 0;
              public int SECONDARY_BRANCH= 1;
              public int STORE_BRANCH= 1;
       }
      
       public interface ResponsibilitiesConstants {
              public int MANAGEMENT= 0;
              public int MAINTENANCE= 1;
              public int REVIEWING= 1;
              public int DOCUMENTATION= 1;
             
       }
}

AbstractManager:
public abstract class AbstractManager extends AbstractEmploye {

       public int[] getResponsibilities(){
              return new int[]{ResponsibilitiesConstants.MANAGEMENT,                                                                      ResponsibilitiesConstants.DOCUMENTATION};
       }
             
       public int getDepartment(){
              return DepartmentConstants.MANAGEMENT_DEPARTMENT;
       }
}

TechnicalManager:
public abstract class TechnicalManager extends AbstractManager {

       public int[] getResponsibilities(){
              return new int[]{ResponsibilitiesConstants.MANAGEMENT, ResponsibilitiesConstants.REVIEWING};
       }
             
       public int getDepartment(){
              return isMaintenance()? DepartmentConstants.MAINTENANCE_DEPARTMENT : DepartmentConstants.MANAGEMENT_DEPARTMENT;
       }
      
       public int getBranch(){
              return this.isLocal()?BranchesConstants.SECONDARY_BRANCH:BranchesConstants.MAIN_BRANCH;
       }
      
       public boolean isMaintenance(){
              return this.isLocal();
       }
      
       public abstract boolean isLocal();
}



Now, I realize I want to pull up the methods isLocal, isMaintainane, and getBranch from the TechnicalManager class and place them into its parent AbstractManager. In eclipse, we can just right click on any of these methods. Let’s say getDepartment, and choose pull up as follows:


Then, in the refactoring wizard you will be informed that the focused methods are about to be pulled up, which is good):


Hit next, and you will be informed that there is a missing method and it must be moved along with your getDepartment method, which is a good usability:
 

Click Back.and select isMaintainance method since it is required to avoid any compilation error.
 
Hit next, but this time you will be informed that there is still a method to be included, which is isLocal:



Return back and add isLocal. Hit next. This time, you will be informed that getDepartment already exists and you cannot pull it up:
 

If we hit finish, we will end up with these errors in AbstractManager:
 
Discussion:
The pre-mentioned scenario reveals three major usability issues. In addition, a neat enhancement is included as follows:

Problem1:
The wizard can only tell which method is still missing from the method to be pulled up if it is only referenced by it. However, it does not go through each method to be included and see if it also has some other references for other methods. For instance,
Method1 uses Method2. Method2 uses Method3. Method3 uses Method4.
The expectation here is when I try to pull up Method1; I learn that Method2, Method3, and Method4 must be pulled up as well since they are referenced together. But what happens here is that we only suggest Method2. Then, I realize that Method2 needs Method3 and so on.
This problem becomes more complex when you are working on a large project with more complicated hierarchies.

Problem2:
When I want to pull a method up to a parent Abstract class, I am expecting this method to become the new parent method to be inherited by my sub classes. This means that I will expect the abstract getDepartment method in AbstractManager to be removed because we are going to have an implemented getDepartment method come from one of its sub-classes. However, what happens here is eclipse does not recognize it is the same method given and that the sub-class method is not abstract. The bottom line here is both methods have the same signature, but we are going to implement that abstract method. However, this is not the case in eclipse.

Problem3:
Assume that the method getDepartment was not an abstract method. So, pulling up the sub-class method getDepartment will mean that this will result in a duplicate method. But in this case, it will be a positive warning. However, the usability issue I realize here is that after going back and forth in next/back buttons to include the missing methods I was not able to pull up the main method from the beginning. The proper usability here is to inform the user right away that this method already exists in the parent, and you will have name collisions here.

Enhancement suggestion:
One of the handy solutions we can get when working on abstract classes, especially for the deeply nested abstract class, is to know where I would like to put the new abstract method definition. For instance, I can have the following hierarchy:
Class1
SubClass1
SubClass1SubClass1
SubClass1SubClass1SubClass1

I have a method “someMethod” in the last class in the hierarchy SubClass1SubClass1SubClass1. I would like to pull it up. I would expect a wizard to ask me if I would like to make an abstract method. I am given the chance to decide where I should place it. So, if I said Class1, then it will be put in Class1, and has its implementation in my end sub class.

This is not only required for abstract classes, but also required for interfaces.

Conclusion:
The pull-up feature in eclipse is very easy, It provides a user-friendly wizard, but it needs some work on the abstract classes’ end.

No comments:

Post a Comment