The main object of this research is the refactoring of existing software. This chapter gives some definitions of used terms. It explores the ideas and the ways how code changes can be done faster and easier, and how they can be automated using special kinds of components (wizards/transformations).
3.1 Principles in refactorings
Refactoring is a fast and safe way to transform an existing program.
· Fast, because it automatically changes program code and saves time
spent typing and fixing new bugs.
· Safe, because it guarantees the preservation of program behavior.
3.1.1 Definition of refactoring
The main purpose of refactoring is to make existing software more flexible, easier to extend, better structured and more understandable. A good definition for refactoring was made by Ralph Johnson [Johnson95]:
„Refactoring is a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior“
The purpose of this research is to explore refactorings which can be done or supported by a transformation tool. We would call it: „a change made automatically to the internal structure of software...“ In this paper we will use the term: „transformation“ as the implementation of a particular refactoring type.
3.1.2 Properties of refactoring
The main property of a refactoring is the preservation of the program behavior. It doesn't matter if refactoring is used to make a program more flexible, or easier to extend or just cleans up the code - the program functionality does not change. This property makes refactorings a good and „safe“ way to transform the software.(see section 4.1.4)
The basic parts of a refactoring description are very well introduced by Martin Fowler
in his book [Fowler99] „Refactorings“:
- Name (used to identify a particular refactoring)
- Description(what it does and when to use it)
- Motivation(describes why it is good to use it)
- Mechanics(step by step description of the refactoring process)
Examples(simple example for this kind of refactoring)
The name of the refactoring should introduce what this refactoring does (like “Create Class“), and the refactoring-description in some cases could be better done as an UML-diagram (start- and end- state).
3.2 Use of refactorings
Software development is an expensive process. This fact motivates reuse and evolution of existing software via refactoring.
3.2.1 Refactorings for reuse
The example described in section 2.2 is a good demonstration for the reuse of software:
- The program was restructured and was made more flexible, before it was extended.
- Some new functionality was added to the program. (e.g. new engine for a car)
- Some program modules were removed (e.g. old engine)
William Opdyke refers in his thesis „Refactoring object-oriented frameworks“
[Opdyke92] to four important aspects for the reuse of software:
- Finding a reusable component
- Understanding the component
- Modifying a component
- Composing the components together
He describes these as complex mental tasks which „do not happen by accident“. The refactoring-example above is not very difficult to understand, nevertheless one has to plan these transformations, to find what can be reused and how to extend the program. This task could prove to be very difficult, because one has to understand the code of the existing program!
3.2.2 Refactorings during the implementation
Sometimes programmers refactor their own source code during its development. By adding new functionality, for example, the programmer realizes this would be much easier if the code would be structured differently. The programmer could also find duplicate code while implementing his program and may decide to extract it into a new class.
Often a good design idea for a particular problem comes after the programmer has already implemented a „strange“ solution. Also, a „last minute request“ can make your well-designed implementation useless!
In the above examples the programmer starts to refactor his program. Usually with a „copy-and-paste“ operation! How can a refactoring-tool help the developer transform his program?
- A tool can give an overview of an existing solution. Information about a design-pattern
could bring the programmer to a new design idea.
- A tool can check the conditions(or show the information about the conditions) for a
code-change. This will help to avoid errors and helps in choosing the right solution
- A tool can save time spent typing(and finding new syntax errors!) by performing those
Example: We have an interface with several implementations. A new functionality has
to be added to this interface. This method will be used only by one implementation.
Problem: By adding a new method to the interface this method must be supported by all
implementations of the interface.
- Derive this interface to a new extended interface with one implementation(so far)
- Create an abstract class with default implementation of new method and let this class
be super-class for all implementations of interface
- Extend all implementations with this method.
Benefit: The developer can use a refactoring-tool to create and test a chosen solution.
Another interesting idea could be the integration of a refactoring-tool in a UML-tool such as Rational Rose or STP. UML tools can use refactoring-features for „reverse engineering“. Example: If a class has been moved in a „Class-diagram“ of the UML-tool this tool can use refactoring „Move Class“ to update the source code.
3.2.3 Refactorings for maintenance
Very similar to software implementation and reuse is software maintenance. Refactoring can also be used for the maintenance of software.
Example: Restructuring in the new version of a program can be done automatically by a refactoring tool without changing the behavior of the whole program. Refactoring can be used to „clean up“ the code and can make the program easier to understand which may then help you to find the bugs.
A transformation is an implementation of a concrete refactoring. A transformation is a process which:
1) takes the current program source code as input (e.g. DPT gets symbols from parser)
2) requests and gets parameters from user (DPT - user interface)
3) generates code changes(DPT - code generators)
(Figure 3.1 transformation)
3.3.1 Scheme for transformations
The scheme of a transformation contains:
· Description of the transformation
A verbal description should include start and end states of the transformation. Information about program changes, preconditions, parameters should be available. Motivation, examples, mechanics of the transformation can be added to the description
(DPT-shows description as help-information).
· Preconditions which should be accomplished
There are two kinds of preconditions:
1) Necessary conditions:
Transformation can’t be completed if condition is not accomplished.
Example: Transformation „New class“ : Class name must not exist.
2) Minor conditions: The compilancy with minor conditions is necessary to preserve program behavior(i.e. transformation can be completed, but program behavior
will change if the condition is not accomplished)
Example: Transformation „Copy method“ : If method is abstract then
target class must be abstract, too (see section 4.1.4).
· Parameters of the transformation
There are two kinds of parameters:
1) Fixed parameters: Required parameters.
Example: Transformation „Create method “ Parameter = “Name of the method“
2) Variable list of optional parameters:
Example: Transformation „Create method “ Parameter = “List of parameters“
· Output of the transformation
Output can be used to construct a composite-transformation(see section 4.4).
Output contains the type of entity and entity identifier
Example: Transformation = „Create class“
Output-type = “class“
Output-Identifier = “com.ui.NewClass“
3.3.2 Transformation properties
There are two kinds of transformations:
1) Single transformation (one step transformation). Example: “Move Method“
2) Multiple transformation which could be the multiple(simultaneous) execution of a single transformation. (Example: “Move all public methods“) or a composition of more than one single transformation (see section 4.1.5)
Other important property is the safety of the transformation. The transformation is „safe“, if it does not change behavior of the program. Only a „safe“ transformation implements a refactoring (see section 4.1.4). One of the most important tasks for the transformation is checking the conditions to preserve the behavior of the program. It would be nice if a transformation could check all conditions. This feature depends on the implementation of the transformation-tool. One of the most difficult problems is finding all references of a program entity (see section 4.1.4).
If a transformation tool is included in the development suite, then it can use the compiler information to resolve all references to a given symbol. In this case the user does not need to check the preconditions.
If a transformation tool is a small stand-alone program(like DPT) with only parsers for given languages, it does not and probably should not resolve all references to a entity. In this case some of the preconditions should be checked by the user.
Example: Transformation = “Delete method“ Condition = “Method is unreferenced“
After the transformation the user can run his compiler and will get „undefined symbol found“ if there were some hidden references to this method. (see also section 4.2.2)
3.4 Tools for refactoring and wizards
There are very few tools to support refactorings. (Example: „Refactoring Browser“ for Smalltalk). Some of the software development tools offer a few basic refactoring -transformations (referred as „Code-Wizards“).
Examples: „Implement interface“-wizard (JBuilder) or „New Class“ wizard (MSD).
The term „Wizard“ comes from software development environments like MSD/Jbuilder.
Examples: LayoutWizard, ClassWizard, DeploymentWizard, DocWizard,...
A wizard is basically a creational code generator which generates particular program-changes. Many wizards are designed to allow the user to control(e.g. parameter setting) the execution process: they usually contain a GUI-Interface for interaction with the user.
The wizard could also guarantee the preserving behavior of the program. In this case a wizard is a refactoring-transformation. There are very few wizards to support refactorings for strongly typed languages like C/C++ or Java.
Often wizards offer a very intuitive GUI with navigational setting of the parameters and simple action handling. Also some wizards do not require the knowledge of the whole program-code bundle and external dependencies to complete a simple task. All of those wizard-features could be used for design of a refactoring-tool.
!-- *** -->