Chapter 4

Design of transformation-catalog

 

One of the most important tasks for the development of a refactoring-tool is the definition of the transformation catalog. In refactoring books[Fowler99],[Opdyke92] one can find hundreds of program-transformation patterns. It is important to select useful transformations (this calls for a definition of the term „useful“) and to select transformations which can be useful in composed transformations.

 

4.1 Criteria for a transformation

 

The first and most important criterion is the use of a transformation. There is a fine difference between defining a transformation for a refactoring-tool and defining a transformation to show that these kinds of refactorings can be done automatically and to prove its correctness. This difference depends on human nature.  Users of a refactoring-

tool are programmers and they are human beings with their strengths and weaknesses.

 

4.1.1 Usability of the transformation

 

Definition: A transformation is useful if it can really save time during program development. This can be time needed for finding the references and typing ( see section 4.3.3.1  „implements interface“) or can save time for debugging the program.

Example: ( for a less useful transformation): Transformation „extract part of method body to new method“ (In most cases, programmers do these changes by hand)

 

4.1.2 Reduction of the user interaction

 

The fact is that programmers are very impatient users. This implies that although a particular transformation in the catalog is useful, it may never be used by programmer, because the execution of the transformation takes too much time. In other words the interaction with the user has to be short. This implies following consequences:

-  avoidance of transformations with many parameters in the catalog:

Example (a less useful transformation): „Create a particular class with several members“

Parameters: Class-name, super class, implemented interfaces, class-variables and their initialization, class-methods and their names, parameters(-type /-position).

It is faster to create this class by hand than to set the parameters for this transformation!

- avoidance of the transformations with many(or not automatically checked) preconditions in the catalog:

Example (a less useful transformation): Change a class to singleton. ( Description -singleton: There exists exactly one instance of this class at any time). User has to make sure that no data loss will occur due to destruction of the object. (Note: singleton-transformation is not a refactoring. It changes the behavior of the program)

 

4.1.3 Use of multiple transformations

 

A good refactoring catalog should  provide a „multiple-transformations“ feature to increase the speed of the transformation process. There are two kinds of multiple transformations:

 - Composed transformations: A composition(sequence) of different transformations which can be composed by the user. The output-parameter from the last executed  transformation is passed as input-parameter to the next transformation. The execution of this transformation is sequential. (See more in section 4.4)

 - Multiple version of one standard transformation. Instead of executing one simple single operation like „move/copy public member to super/class“ many times it would be convenient to have a transformation like „move/copy multiple members to super/class“. The execution of this transformation can be done simultaneously. This is a very important aspect for the realization: Instead of adding the transformation parameters many times by executing the same transformation many times (via composition : „Move“ 10 times a member) the user can specify a property that is common for all entities which should be passed as parameter(via multiple: „Move all“ + „public“ members) and the transformation will be executed once.

 

4.1.4 Safety of the transformation

 

The main condition for a refactoring is the preservation of the program behavior. A transformation is „safe“ if this condition is accomplished. This implies that a transformation must be canceled if it does not preserve the program behavior. The question is: Who is in charge of canceling a transformation?!

In the usual development process of a new program (e.g. implementation of a new method) the state of the program code is most often not safe (e.g. as long as the  implementation of a new entity is not completed the program can not be compiled). In some cases (especially for composed transformations) it could be more useful, or better to say, user-friendly if a transformation would inform the user about the occurrence of a particular(minor) problem and would then ask the user for the next step.

Example: User composed(two steps) transformation: „Copy method“+„Change class to abstract“. If the method is abstract and the target class isn’t, the transformation has to stop because the target class is not abstract(so far). But the transformation would be „safe“ if one would look at this transformation as a single transformation.

Conclusion: It would be more user-friendly to let the user decide „when to stop“ the transformation if a conflict occurs. The transformation-step will not be „refactoring“ at the moment that the user decides to continue (code-state is „unsafe“), but it will be „safe“ after the next step „Change class to abstract“ and it would save time (time to create new user transformation: „Change class to abstract“ + „Copy method“) and saving development time is one of the main goals of refactoring.

 

Design decision for DPT-Tool:

 

A transformation informs the user about occurred(minor) conflicts and possible changes of program-behavior and gives the user a possibility to either break the transformation without changes or to continue the transformation. Not every condition is minor!

Example: Copy method to class. If the target class does not exist, the method can't be copied.   

 

4.1.5 Complex transformations.

 

There are two kinds of complex transformations:

1)      Transformation with many transformation steps (e.g. particular subclass creation)

2)      Transformations with high abstraction level (e.g. „Abstract Factory“ pattern) 

 

The most essential criteria to include a transformation in a refactoring catalog are:

Ţ  Knowledge about user expectations and expected users.

Ţ  User has to know how, when and what to transform.

 

- User has to understand what a particular  transformation does

The catalog could have hundreds of very good and useful transformations, but if the user doesn't know what they are doing or when to use a particular refactoring it will not do the user any good!

 

 

- User has to understand correctly what a particular transformation does:

Different people may have different opinions about the meaning of  the same term (e.g. „Server/Client“) A good Help-system could help to avoid this problem. Since the user readiness to read additional documentation is very low, a standard catalog should not include transformations which could be misinterpreted by the user.

Examples: In several refactoring books [Batory99] one can find the transformation „Reassociate“- always with a different description. But every programmer can imagine what the transformation „Create new class“ or  „implements an interface“  does.

 

Conclusion:  Avoid the complex transformations while designing the standard catalog for the refactoring tool. Let the user create his own „monster“- transformations. (See also section 4.4.2  „composition of transformations“)

 

4.2 Transformation environment

 

Until this point we have described the transformation at a high abstraction level and independent from the development environment.The design of a concrete transformation catalog depends on the chosen environment such as the language of the source code, the compiler version and options (especially for C++), or how far the tool supports a particular transformation. The transformation should not be dependent on the development platform( Unix/ Windows). This section describes how important the environment properties are for the design of the refactoring catalog. 

 

4.2.1 Source code language

 

The source code is the object of the transformation. The choice of a programming language is significant for the design of the refactoring-catalog. Although some of the programming languages look very similar(like C++ and Java) they differ not only in syntax-grammar, but also by using different constructs. The transformation defined for one language(e.g. „create interface“ for Java) can not be applied to an other language

(e.g. C++ does not have interfaces). In some cases a very similar transformation such as „inherit“ has different conditions and contexts for an other language (e.g. C++ supports multiple-inheritance, Java does not).  

 

The refactoring catalog in this research has been defined for the programming language Java. Java is one of the most popular object-oriented programming languages at this time. It is freely available and is independent from the development-platform. Java is strongly typified and statically compiled. The development of the programming environments and tools to support the developer(especially for refactoring) is in process. 

 

4.2.2 Tool support 

 

The question of how far a refactoring-tool should support the transformation may need a separate discussion. At this point some aspects which are relevant for this research will be introduced.

It would be helpful if a refactoring tool could check all of the preconditions of the transformation. For this task the tool would need to resolve all symbol references of the program. This implies the availability of a complete program code(which is not always available!) The tool also needs an extremely good parser to resolve all the references correctly. Finding the references isn’t an easy task and in many cases a compiler would be more appropriate for this job(especially in resolving external references).

However, it would be easier to support the whole transformation process if the refactoring tool is a part of the development environment. In this case the tool could use the IDE of the project for finding all of the sources and the tool could use the compiler/parser for finding all of the references of a symbol in  the program-code.

 

Another way to handle this would be to split the conditions for a particular transformation into:

1)      Preconditions that can be checked by the refactoring tool.

2)      Condition that should be checked by user(user can use compiler to find references).

 

Contras: - In this case the tool does not check all of the preconditions of the refactoring.

- User action is required, therefore more interaction with the user will be needed.

Pros:   - The tool is not dependent on the development environment.

            - The transformation can be more easily applied to the part of the project.

 

Example:   A transformation creates a method and one of the method-parameters is „String“.  If  the tool can not find the implementation of this class in the program code, it handles the symbol as an external unresolved symbol and continues the execution. After the transformation the user can check the behavior of the program by simply recompiling of his program. This process is more appropriate for small „stand-alone“-tools and can also be used for „non-refactoring“ transformations which change the behavior of the program(see section 4.1.4).