Sunteți pe pagina 1din 44

A Pattern for Refactoring

Detectors

Anastasios Tsimakis

Diploma Thesis

Supervisor: Apostolos Zarras

Ioannina, February 2018

ΤΜΗΜΑ ΜΗΧ. Η/Υ & ΠΛΗΡΟΦΟΡΙΚΗΣ


ΠΑΝΕΠΙΣΤΗΜΙΟ ΙΩΑΝΝΙΝΩΝ

DEPARTMENT OF COMPUTER SCIENCE & ENGINEERING


UNIVERSITY OF IOANNINA
Prologue
This Diploma thesis was carried out in the Department of Computer Science and
Engineering at the University of Ioannina.

I am grateful to all who supported me in this endeavor, including Professor


Panagiotis Vassiliadis and Professor Kostas Magoutis for their valuable suggestions
during the evaluation phase.

I am especially grateful to Professor Apostolos Zarras, who supervised this project


and guided me until the completion of this work. His advises and suggestions were
always precise and have been appreciated a lot. He really helped me to understand
the topic and his assistance was always literally helpful and accurate.

February, 2018

Anastasios Tsimakis
Abstract
Refactoring tools have had a significant presence in software engineering for many
years. However, they are often not utilized to their fullest potential, and studies have
outlined that one of their issues is the difficulty of freely extending their capabilities
with new refactorings. In this work, we propose a pattern for refactoring detectors
in order to overcome that obstacle. In addition, the pattern was applied to an already
existing refactoring tool to showcase its properties.

Keywords: Refactoring, Tool, Detector, Pattern


Table of Contents
Table of Contents ............................................................................................................................................... 4

Chapter 1. Introduction .................................................................................................................................. 5

1.1 Thesis Goal......................................................................................................................................... 5

1.2 Chapter Organization .................................................................................................................... 6

Chapter 2. Related Work ................................................................................................................................ 7

2.1 Refactoring TripAdvisor............................................................................................................... 7

2.2 Patterns .............................................................................................................................................15

Chapter 3. Refactoring Detector Pattern ................................................................................................17

Chapter 4. Case Study.....................................................................................................................................25

Chapter 5. Conclusions ..................................................................................................................................42

Chapter 6. References....................................................................................................................................43
Chapter 1. Introduction

1.1 Thesis Goal


Refactoring is a disciplined technique for restructuring an existing body of code,
altering its internal structure without changing its external behavior, usually
referring to Object-Oriented Languages. It plays a large role in preserving and
improving code quality, and most importantly, it is a vital part of assuring
maintainability and extensibility. The concept was first introduced by William
Opdyke in his PhD thesis in 1992 [O92], and further refined and expanded by Martin
Fowler in his book “Refactoring: Improving the Design of Existing Code” [F99].

However, refactoring large amounts of code can be a difficult process, taking up a lot
of time and effort. For that reason, several automated tools have been developed,
allowing quick and efficient identification of code blocks in need of refactoring.
Nevertheless, it has been found in studies [MHPB11] that such tools are most of the
time not utilized by developers, for various reasons. In the University of Ioannina,
Theofanis Vartziotis developed one such tool by the name of Refactoring
TripAdvisor [V16], aiming to assist developers with better understanding of
refactorings, as well as automatic detection of opportunities for some refactorings.

Along with the tool, a generalized algorithm for refactoring detectors was
developed, with the main intent to make the tool extensible for future additions.
However, this algorithm was neither fully clarified nor implemented. The goal of
this thesis is twofold: First, to fully develop and refine the algorithm in the form of a
pattern, and secondly to implement it in the code of Refactoring TripAdvisor, to

5
showcase the quality increase of the existing code after application of the proposed
pattern.

1.2 Chapter Organization


The thesis contains the following chapters: Initially in Chapter 2 we discuss
previous work that is related to the thesis, mainly functional details about
Refactoring TripAdvisor and some background on patterns. Chapter 3 contains the
actual refactoring detector pattern, as well as some further explanations and
clarifications. Chapter 4 contains the results of applying the pattern to Refactoring
TripAdvisor's source code, while finally Chapter 5 is the epilogue, along with some
conclusions.

6
Chapter 2. Related Work

2.1 Refactoring TripAdvisor


Manual refactoring is usually a time-consuming process, requiring attention both to
identify possible code segments in need of refactoring, and to ensure that program
behavior remains unchanged. To assist with that, there exist a number of automated
or semi-automated tools, which detect and apply refactorings when prompted by a
user.

One of those tools is Refactoring TripAdvisor (from hereon referenced as RTA), a


semi-automated tool designed for use in Java projects developed in the Eclipse IDE
by Theofanis Vartziotis, as part of his postgraduate dissertation. RTA has two main
goals: Firstly, to provide the theoretical background of each refactoring in order to
assist the user to understand its importance and use, and secondly to automatically
detect code blocks that are applicable for refactoring. To go into more detail, RTA
contains a visualised map of 68 refactorings mentioned in Fowler’s catalog, divided
in six categories according to their goal: Method Composition, Method Call
Improvement, Conditional Expression Simplification, Generalisation Improvement,
Data Organisation, and Feature Movement Between Objects. In addition, there are
three types of relationships between refactorings that are defined in the
dissertation: Succession, Part Of, and Instead Of, denoting respectively that a
refactoring is usually applied after another one, a refactoring is combined with
others to form a more complex, composite refactoring, and that a refactoring is an
alternative option to another. These relationship types are used to create an
interlinked graph, using refactorings as vertices and relationships as edges. Figures
2.2 and 2.3 contain examples of those graphs, while Figure 2.1 explains the color-
coding.

7
Figure 2.1: Color-coding of refactoring categories

Figure 2.2: Method Composition relationship map

8
Figure 2.3: Feature Movement Between Objects relationship map

Succession relations are indicated by a straight line with an arrow, “Part Of”
relations are indicated by a dashed line with an arrow, and finally “Instead of”
relations are indicated by a dashed line with arrows on both ends.

For each of the refactorings, additional details are provided, such as the motivation
for applying that refactoring, as well as a short code snippet before and after
application. An example of this is provided in Figures 2.4 and 2.5.

9
Figure 2.4: Motivation for using the Replace Temp with Query refactoring

Figure 2.5: Example of applying the Replace Temp with Query refactoring

Our main focus, however, lies with the design and implementation of the automated
refactoring opportunity detectors. Not all the refactorings have implemented

10
detectors; of the 68 included in the map, only 11 have automatic opportunity
detection capabilities. They are listed in Table 2.1 below, along with their categories.

Refactoring Category

Extract Method Method Composition

Inline Method Method Composition

Inline Temp Method Composition

Replace Temp with Query Method Composition

Introduce Explaining Variable Method Composition

Split Temporary Variable Method Composition

Remove Assignments to Parameters Method Composition

Replace Method with Method Object Method Composition

Move Method Feature Movement Between Objects

Extract Class Feature Movement Between Objects

Introduce Parameter Object Method Call Improvement

Table 2.1: List of implemented refactoring detectors

In the original dissertation, T. Vartziotis describes a generalized algorithm for


detection of refactoring opportunities (page 52), displayed in Figure 2.6:

11
identifyRefactoringOpportunities(EclipseProject p)

for each JavaFile file in p

t = getASTTree(file)

for each Class class in t

for each Method method in class

suggestedEntit =identificationForSpecificRefactoring(Method
method)

return(suggestedEntity)

Figure 2.6: Opportunity detection algorithm

This algorithm was expanded and refined later in a conference paper [VZV15],
where it took the following form given in figure 2.7:

Require: AST(VAST, EAST)

Ensure: ∀ o ∈ O, o ∈ AST

1: scope ← identifyScope(AST);

2: S ← identifySubjects(scope);

3: for all subject ∈ S do

4: O ← O ∪ identifyOpportunities(subject);

5: end for

6: visualizeOpportunities(O);

7: return;

Figure 2.7: Updated form of detector algorithm

Quoting the explanation from the aforementioned paper:

“[...] a refactoring detector takes as input an abstract syntax tree AST that represents a
software project; the nodes of the tree correspond to specific program structures,
while the edges denote structural relations between these structures. The detection of
refactoring opportunities is a four steps process. The first step of the process
(Algorithm 1 line 1) identifies in the given AST the scope of the refactoring, i.e., the

12
part of the project that the developer is working with (e.g., a selected package, class,
method). The second step of the process (Algorithm 1 line 2) identifies, within the
refactoring scope, the refactoring subjects S, i.e., the specific structures within the
refactoring scope that can be refactored (e.g. the methods of the class that can be
simplified with Extract Method). The third step (Algorithm 1 lines 2-3) identifies a set
of refactoring opportunities O for each subject (e.g., the computational slices of a
method that can become new methods). Finally, the last step of the process (Algorithm
1 line 6) visualizes the identified refactoring opportunities.” (p. 6)

The purpose of this algorithm was to facilitate the ability to continue work on the
tool, expanding its abilities with the addition of further refactoring detectors, either
developed in-house or by third parties. This is especially important, since of the 11
detectors implemented, 4 of them rely on opportunity identification algorithms of
third-party developers. In particular, Extract Class [FTSC12], Extract Method
[TC09], Move Method, and Replace Method with Method Object utilise algorithms
used in the JDeodorant plugin by N. Tsantalis, A. Chatzigeorgiou, et al [TC14].

In general, the problem of tool extensibility is recognised in multiple studies and


papers; in a paper by Tom Mens et al [M03] it was mentioned as a future research
subject, quoting the insufficiency of existing extensibility mechanisms. In 2002,
Antonio Menezes Leitao proposed the use of a pattern language to assist with
defining refactorings for LISP programs, with the goal of allowing tools to easily
incorporate new or composite refactorings [L02]. In a similar vein, Huiqing Li and
Simon Thompson created a framework to define refactorings for Erlang programs in
2012 [LT12]. However, both of these are language-dependent, and focus purely on
rigidly defining primitive refactorings, and using them to form more complicated
ones, while also allowing the users of the tool to create their own composite
refactorings. On the contrary, the algorithm mentioned above is mostly language
independent, and completely independent of the refactoring itself, since the
opportunity identification is left completely in the hands of the tool developer.

The main issue with the algorithm is that, while applicable for every detector
regardless of refactoring, is quite vague with its definitions of scope, subject, and

13
opportunity. There is no formal specification of how to identify subjects, or why
they are needed. In addition, while the algorithm was implemented in the RTA
source code, it was not in an easily recognisable form, and was repeated in every
single detector class, rather than in one superclass, leading to very large amounts of
duplicated code. Not only that, but RTA also had other quality issues, such as very
long methods, and code duplication unrelated to the above algorithm. These
problems are illustrated in detail in Chapter 4.

14
2.2 Patterns
Design patterns were first introduced in 1977 by an architect by the name of
Christopher Alexander [A77] and were focused in the disciplines of architecture and
civil engineering, but later found widespread application in the field of software
engineering as well as other sciences [BC87]. In its basest form, a pattern is a set of
rules describing the relations between a context, a problem (or a set of forces) that
occurs repeatedly in that context, and a solution for the problem. We will be using
this concept to propose a generalized form for refactoring detectors.

In 1997, Gerard Meszaros and Jim Doble laid the foundations for formalized pattern
writing, devising a pattern language for pattern writing in the book “Pattern
Languages of Program Design” [MD97]. Later, in 2004, Neil Harrison expanded on
this basis with a paper submitted in the European Conference on pattern Languages
of Programs (EuroPLoP) titled “Advanced Pattern Writing” [H04]. There are several
different forms of patterns, and while each author can create their own or adjust
existing ones at will, there are a few that have been studied and refined over the
years. Two of the more notable ones are the Alexandrian Form (the original one
proposed by Christopher Alexander), and Gang of Four Form, appearing in their
well-known book “Design Patterns: Elements of Reusable Object-Oriented Software”
[GOF94] in 1994. In this thesis, we will focus on a form by James Coplien, based on
the Alexandrian Form. In 2011, Wellhausen and Fießer [WF11] published a paper in
EuroPLoP with a step-by-step guide on how to write a design pattern in this form.

Below there is a quick overview and explanation of each of the pattern components
for the Coplien Form.

The Context is a set of circumstances in which a problem exists.

The Problem is the specific problem that the pattern attempts to solve.

The Forces are a set of conditions that magnify the problem or inhibit its solution.

The Solution describes the proposed solution to the problem, taking into account
the given context and forces.

15
The Consequences describe what happens after application of the solution. They
are usually divided into positive consequences, or Benefits, and negative
consequences, or Liabilities.

16
Chapter 3. Refactoring Detector

Pattern

This chapter contains the formalized pattern for the refactoring detectors, as well as
some further explanations.

Context:

You want to create a tool to assist with the refactoring process of a software project.

Problem:

Given the large number of different refactorings, it would be tedious and impractical
to design a detector from the ground up for each one of them.

Forces:

Genericness: We want the detectors to follow a similar form independent of the


refactoring they implement, in order to reduce the overall code volume and
streamline the detector implementation process.

Extensibility: We want our tool to be able to incorporate further detectors developed


in the future, either from us or from third party developers.

Solution:

We propose a generalised 3-step algorithm that all detectors follow.

17
1. Identify Refactoring Scope

2. Identify Refactoring Subjects

3. Identify Refactoring Opportunities

The first step is identifying the refactoring scope, which is the region of the software
project that the user wants to search for possible instances of code that require
refactoring. The scope can be divided in clearly defined tiers, each one broader than
the next.

The second step is identifying the refactoring subjects within the given scope. Each
refactoring has a subject type, which is the smallest self-contained block of code that
refactoring opportunities can be detected in, or, more abstractly, where the
refactoring is applied. As such, concerning Object-Oriented languages, there are only
two subject types: Methods and Classes. Table 3.1 contains the refactoring subject
and opportunity types of all the refactorings in RTA's map, divided according to
their category.

The final step is identifying the actual refactoring opportunities. These are code
blocks or structures that exist within the refactoring subjects that fulfil all criteria
for the refactoring in question to be applied. While some refactorings may have
similar opportunities in terms of the code structures that will be affected by the
different refactorings, there is simply too much variance to have practically usable
groups. The table with subject types mentioned above also contains opportunity
types for the refactorings included, to illustrate this issue.

Consequences

Benefits:

 Genericness: The pattern is independent of the refactoring itself, which only


comes into play in the last step of Opportunity Identification. Not only that, it
is also mostly independent of the language used as well.

18
 Simplicity: The 3 steps presented in the solution are clear, concise, and
straightforward.

Liabilities:

 The pattern encounters difficulties when attempting to combine two already


existing implementations with each other.

Implementation

Below we show a generic implementation of the pattern, in the form of a UML


diagram (Figure 3.1)

Figure 3.1

19
The RefactoringDetector superclass contains the code for scope identification that
remains the same across all refactorings. The two subject subclasses implement the
abstract method for subject identification, and also store all subjects found in the
subjectList field. Finally, a number of individual refactoring detectors, each with
their own opportunity type, extend their appropriate subject class and implement
the refactoring opportunity detection algorithm. (The “...” in the parameter list of
identifyOpportunities signify that they have a variable number of parameters, as
some of them might be reliant on thresholds).

While the steps of the pattern algorithm might make sense intrinsically, it is not
immediately obvious why they are needed. Concerning the use of the concept of
scope, scanning an entire project for refactorings might return hundreds if not
thousands of results, which would be cumbersome for the user to go through. Not
only that, but for some parts of the project, changes might be unwelcome or even
prohibited, so showing possible refactorings on those would only hinder the user.
As for subjects, they assist greatly in categorising refactorings and reducing code
duplication. As mentioned in the solution, the two subject types are Methods and
Classes. While there is no absolute rule for assigning subject types to refactorings,
especially since some of them are vaguely defined (for example Substitute
Algorithm), a good predictor is the refactoring's “range”, or the area of code it will
affect. For example, refactorings that deal with local variables, parameters,
conditionals, and generally the internal workings of methods, have mostly (with
very few exceptions: for example, Introduce Parameter Object has a Class subject
even though it concerns parameters) Method subjects. On the other hand,
refactorings that have to do with fields and inter-class relations (such as inheritance
or association) mostly have Class subjects. A full list of 69 refactorings, their subject
types, as well as opportunity types is present below, in Table 3.1, divided according
to category.

20
Refactoring Subject Type Opportunity Type
Method Composition

Extract Method Method Method slice

Inline Method Method Method declaration

Inline Temp Method Variable

Replace Temp with Query Method Variable

Introduce Explaining Variable Method Expression

Split Temporary Variable Method Variable

Remove Assignments to Parameters Method Parameter

Replace Method with Method Object Method Method declaration

Substitute Algorithm Class Class slice

Refactoring Subject Type Opportunity Type


Feature Movement Between Objects

Move Method Method Method declaration

Move Field Class Field

Extract Class Class Class slice

Inline Class Class Class

Hide Delegate Class Class

Remove Middle Man Class Class

Introdue Foreign Method Class Method declaration

Introduce Local Extention Class Class

21
Refactoring Subject Type Opportunity Type

Data Organization

Self Encapsulate Field Class Field

Replace Data Value with Object Class Class slice

Change Value to Reference Class Object Set

Change Reference to Value Class Class

Replace Array with Object Class Class Slice

Duplicate Observed Data Class Class Slice

Change Unidirectional Association to Class Class

Change Bidirectional Association to Class Class

Replace Magic Number with Symbolic Constant Method Method Slice

Encapsulate Field Class Field

Encapsulate Collection Class Field

Replace Record with Data Class Class Class Slice

Replace Type Code with Class Class Field

Replace Type Code with Subclasses Class Field

Replace Tyle Code with State/Strategy Class Field

Replace Subclass with Fields Class Class

Refactoring Subject Type Opportunity Type


Conditional Expression Simplification

Decompose Conditional Method Conditional

Consolidate Conditional Expression Method Expression


Method slice

Consolidate Duplicate Conditional Fragments Method Method slice

Remove Control Flag Method Variable

Replace Conditional with Guard Clauses Method Method slice

Replace Conditional with Polymorphism Method Method slice

Introduce Null Object Method Conditional


Expression
Conditional
Introduce Assertion Method
Expression

22
Refactoring Subject Type Opportunity Type

Method Call Improvement

Rename Method Method Method Declaration

Add Parameter Method Method Declaration

Remove Parameter Method Parameter

Seperate Query from Modifier Method Method Declaration

Parameterize Method Class Method Declaration

Replace Parameter with Explicit Methods Method Parameter

Preserve Whole Object Method Parameter Set

Replace Parameter with Method Method Parameter Set

Introduce Parameter Object Class Parameter Set

Introduce Parameter Object (RTA Version)* Method Method Declaration

Remove Setting Method Class Method Declaration

Hide Method Method Method Declaration

Replace Constructor with Factory Method Class Method (Constructor)

Encapsulate Downcast Method Method Declaration

Replace Error Code with Exception Method Method Declaration

Replace Exception with Test Method Method Slice


*Refactoring TripAdvisor implements a different version of Introduce Parameter Object,
suggesting refactorings on methods with a large number of parameters, rather than
parameter sets that are common across multiple methods.

23
Refactoring Subject Type Opportunity Type

Generalization Improvement

Pull Up Field Class Field

Pull Up Method Class Method Declaration

Pull Up Constructor Body Class Method (Constuctor)

Push Down Method Method Method Declaration

Push Down Field Class Field

Extract Subclass Class Class Slice

Extract Superclass Class Class Slice

Extract Interface Class Class Slice

Collapse Hierarchy Class Class

Form Template Method Class Class Slice

Replace Inheritance with Delegation Class Class

Replace Delegation with Inheritance Class Class

There are a couple of concerns about how the pattern handles some conflicts of
scope, but there are multiple ways of resolving them, all depending on how the
developer prefers to handle them. Firstly, there is the question of what to do when
the subject of a refactoring is “larger” that it’s scope; for example, with a Class-
subject refactoring, a user couldn’t pick a singular method for scope. Possible
solutions would be to disallow selection of invalid scopes (either re-prompting the
user or displaying an error), or automatically selecting the smallest or largest valid
scope. Secondly, there is the question of handling refactorings that affect classes
beyond the selected scope; for example Move Method. In this case, opportunities
that would affect code beyond the selected scope could be ignored, or presented
with a special warning.

24
Chapter 4. Case Study

In this chapter, we showcase the improvement the Refactoring TripAdvisor code


underwent, when the detector pattern is properly applied.

First, we show the state of RTA's code before application of the pattern. Figure 4.1
contains the code of the RefactoringDetector interface that all detector classes
implement.

public interface RefactoringDetector {

JFrame getDetectorFrame(Object[] dataForIdentification);

boolean opportunitiesFound();

Figure 4.1: RefactoringDetector Interface

It is clear that the interface has almost zero functionality, with no references to any
of the pattern solution steps. Instead, the entire algorithm is implemented in each
individual detector class again and again.

In the following figures, we take a look at the classes responsible for the detection of
Inline Method and Extract Class refactoring opportunities. For the sake of clarity, we
split them into different segments; the first for class declaration and fields, and one

25
for each method. We will then compare them with each other to showcase the issues
present.

public class InlineMethodIdentification implements RefactoringDetector{

private JFrame mainFrame;

private JPanel mainPanel;

private PackageExplorerSelection selectionInfo;

private boolean opportunitiesFound;

private ArrayList<AbstractMethodDeclaration> candidateMethods;

private ArrayList<ClassObject> declaringClasses;

Figure 4.2: Declaration and fields of Inline Method detector class

public InlineMethodIdentification() {

mainFrame = new JFrame();

mainFrame.setTitle("Inline Method Opportunities");

mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

mainFrame.setResizable(false);

mainFrame.setLocation(new Point(200, 100));

mainFrame.setSize(new Dimension(800, 600));

mainFrame.setIconImage(java.awt.Toolkit.getDefaultToolkit().getImage(
getClass().getResource("/images/repair.png")));

mainPanel = new JPanel();

mainPanel.setBorder(new EmptyBorder(5, 5, 5, 5));

mainFrame.setContentPane(mainPanel);

mainPanel.setLayout(null);

opportunitiesFound = true;

Figure 4.3: Constructor of Inline Method Detector class

26
public class ExtractClassIdentification implements RefactoringDetector{

private JFrame mainFrame;

private JPanel mainPanel;

private ArrayList<ExtractClassCandidateRefactoring> candidates;

private ArrayList<String> itemArray;

private ExtractClassCandidateGroup[] extractTable;

private PackageExplorerSelection selectionInfo;

private boolean opportunitiesFound;

Figure 4.4: Declaration and fields of Extract Class detector class

public ExtractClassIdentification() {

mainFrame = new JFrame();

mainFrame.setTitle("Extract Class Opportunities");

mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

mainFrame.setResizable(false);

mainFrame.setLocation(new Point(200, 100));

mainFrame.setSize(new Dimension(800, 600));

mainFrame.setIconImage(java.awt.Toolkit.getDefaultToolkit().getImage(
getClass().getResource("/images/repair.png")));

mainPanel = new JPanel();

mainPanel.setBorder(new EmptyBorder(5, 5, 5, 5));

mainFrame.setContentPane(mainPanel);

mainPanel.setLayout(null);

opportunitiesFound = true;

Figure 4.5: Constructor of Extract Class detector class

27
Comparing Figures 4.2 and 4.3 to Figures 4.4 and 4.5 respectively, we can see that
there is significant amount of duplication; in fact, the entire constructor is exactly
the same, with only one method call having a different parameter in the second line
of the constructor body.

We will ignore some of the methods dedicated to GUI construction and other work
irrelevant to the pattern, and focus on a specific method that contains the algorithm
for scope, subject, and opportunity identification. Due to its very large size (113
lines for Inline Method, 88 lines for Extract Class), we only present the part relevant
to the pattern. Figures 4.6 and 4.7 contain the code roughly corresponding to scope
identification and subject identification respectively in the Inline Method Detector,
while Figure 4.8 corresponds to scope and subject identification for the Extract
Class Detector. We picked these two specific detectors in order to show the
differences between a method subject detector and a class subject detector.

28
final SystemObject systemObject = ASTReader.getSystemObject();
final Set<ClassObject> classObjectsToBeExamined = new LinkedHashSet<ClassObject>();
final Set<AbstractMethodDeclaration> methodObjectsToBeExamined = new
LinkedHashSet<AbstractMethodDeclaration>();

if(selectionInfo.getSelectedPackageFragmentRoot() != null) {
classObjectsToBeExamined.addAll(systemObject.getClassObjects(selectionInfo.ge
tSelectedPackageFragmentRoot()));
}
else if(selectionInfo.getSelectedPackageFragment() != null) {
classObjectsToBeExamined.addAll(systemObject.getClassObjects(selectionInfo.ge
tSelectedPackageFragment()));
}
else if(selectionInfo.getSelectedCompilationUnit() != null) {
classObjectsToBeExamined.addAll(systemObject.getClassObjects(selectionInfo.ge
tSelectedCompilationUnit()));
}
else if(selectionInfo.getSelectedType() != null) {
classObjectsToBeExamined.addAll(systemObject.getClassObjects(selectionInfo.ge
tSelectedType()));
}
else if(selectionInfo.getSelectedMethod() != null) {
AbstractMethodDeclaration methodObject =
systemObject.getMethodObject(selectionInfo.getSelectedMethod());
if(methodObject != null) {
ClassObject declaringClass =
systemObject.getClassObject(methodObject.getClassName());
if(declaringClass != null && !declaringClass.isEnum()
&& !declaringClass.isInterface()
&& methodObject.getMethodBody() != null)
methodObjectsToBeExamined.add(methodObject);
}
}
else {
classObjectsToBeExamined.addAll(systemObject.getClassObjects());
}

Figure 4.6: Method fragment corresponding to Scope identification for Inline Method

29
if(!classObjectsToBeExamined.isEmpty())

for(ClassObject classObject : classObjectsToBeExamined)

if(!classObject.isEnum() && !classObject.isInterface())

ListIterator<MethodObject> methodIterator =
classObject.getMethodIterator();

while(methodIterator.hasNext())

methodObjectsToBeExamined.add(methodIterator.next());

Figure 4.7: Method fragment corresponding to Subject Identification for Inline Method

The two code fragments shown in figures 4.6 and 4.7 are repeated identically in all
detectors for method subject refactorings, placed in a single method along with the
code for opportunity detection. Besides the code duplication, this also leads to very
large, confusing methods that make it hard to figure out which part of the code is
responsible for each part of the algorithm, hindering the extensibility of the project.
Similar for Figure 4.8, which is repeated in all class subject refactoring detectors.

30
SystemObject systemObject = ASTReader.getSystemObject();

Set<ClassObject> classObjectsToBeExamined = new LinkedHashSet<ClassObject>();

if(selectionInfo.getSelectedPackageFragmentRoot() != null) {

classObjectsToBeExamined.addAll(systemObject.getClassObjects(selectionInfo.ge
tSelectedPackageFragmentRoot()));

else if(selectionInfo.getSelectedPackageFragment() != null) {

classObjectsToBeExamined.addAll(systemObject.getClassObjects(selectionInfo.g
etSelectedPackageFragment()));

else if(selectionInfo.getSelectedCompilationUnit() != null) {

classObjectsToBeExamined.addAll(systemObject.getClassObjects(selectionInfo.ge
tSelectedCompilationUnit()));

else if(selectionInfo.getSelectedType() != null) {

classObjectsToBeExamined.addAll(systemObject.getClassObjects(selectionInfo.ge
tSelectedType()));

else {

classObjectsToBeExamined.addAll(systemObject.getClassObjects());

final Set<String> classNamesToBeExamined = new LinkedHashSet<String>();

for(ClassObject classObject : classObjectsToBeExamined) {

if(!classObject.isEnum())

classNamesToBeExamined.add(classObject.getName());

Figure 4.8: Method fragment corresponding to scope and subject identification for Extract
Class

To solve these issues, we apply the pattern, as seen in Figure 3.1. We turn the
RefactoringDetector interface to an abstract superclass, and move up all common
fields, as well as the constructor, as seen in Figures 4.9 and 4.10:

31
public abstract class RefactoringDetector {

protected JFrame mainFrame;

protected JPanel mainPanel;

protected PackageExplorerSelection selectionInfo;

protected boolean opportunitiesFound;

protected ArrayList<scopeType> listOfValidScopes;

protected HashMap<scopeType, Method> reflectionMap;

protected scopeType scope;

Figure 4.9: Decleration and fields of RefactoringDetector superclass

Besides moving up the common fields, we have also added some new ones. The
listOfValidScopes field, as the name implies, contains all valid scopes for a given
subject type; as mentioned in the end of Chapter 3, the chosen scope must always be
larger than the refactoring's subject type. It is initialized in the constructor of
RefactoringDetector, but populated in the constructor of the subject-specific
subclass constructors. The reflectionMap field links each scope tier with a getter
method that returns the actual scope object. This is due to already-existing code that
could not be changed, both in RTA but also in JDeodorant. It is populated in the
method populateReflectionMap, shown in Figure 4.11, and used in the scope
identification method (Figure 4.12).

32
public RefactoringDetector(String title)

mainFrame = new JFrame();

mainFrame.setTitle(title);

mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

mainFrame.setResizable(false);

mainFrame.setLocation(new Point(200, 100));

mainFrame.setSize(new Dimension(800, 600));

mainFrame.setIconImage(java.awt.Toolkit.getDefaultToolkit().

getImage(getClass().getResource("/images/repair.png")));

mainPanel = new JPanel();

mainPanel.setBorder(new EmptyBorder(5, 5, 5, 5));

mainFrame.setContentPane(mainPanel);

mainPanel.setLayout(null);

opportunitiesFound = true;

listOfValidScopes = new ArrayList<scopeType>();

populateReflectionMap();

Figure 4.10: Constructor of RefactoringDetector

33
private void populateReflectionMap()
{
reflectionMap = new HashMap<scopeType, Method>();
Class packageSelection;
try {
packageSelection =
Class.forName("DataHandling.PackageExplorerSelection");
Method selectionGetPackageFragmentRoot =
packageSelection.getDeclaredMethod("getSelectedPackageFragmentRoot");
Method selectionGetPackageFragment =
packageSelection.getDeclaredMethod("getSelectedPackageFragment");
Method selectionGetCompilationUnit =
packageSelection.getDeclaredMethod("getSelectedCompilationUnit");
Method selectionGetType =
packageSelection.getDeclaredMethod("getSelectedType");
Method selectionGetMethod =
packageSelection.getDeclaredMethod("getSelectedMethod");

reflectionMap.put(scopeType.PACKAGE_FRAGMENT_ROOT,
selectionGetPackageFragmentRoot);
reflectionMap.put(scopeType.PACKAGE_FRAGMENT,
selectionGetPackageFragment);
reflectionMap.put(scopeType.COMPILATION_UNIT,
selectionGetCompilationUnit);
reflectionMap.put(scopeType.TYPE, selectionGetType);
reflectionMap.put(scopeType.METHOD, selectionGetMethod);
reflectionMap.put(scopeType.NONE, null);

} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}

Figure 4.11: Method populating the reflectionMap field

34
In Figure 4.12, we see the updated code for scope identification. We removed the
use of chain if-else-if-else through the use of reflection and generic types.

protected<T extends IParent> T identifyScope() throws IllegalAccessException,


IllegalArgumentException, InvocationTargetException, ClassNotFoundException

scope = selectionInfo.getSelectedScope();

Class packageSelection = Class.forName("PackageExplorerSelection");

if (listOfValidScopes.contains(scope) && scope != scopeType.NONE)

return (T) reflectionMap.get(scope).invoke(selectionInfo);

else

scope = scopeType.NONE;

return null;

Figure 4.12: Scope identification method in RefactoringDetector superclass

Finally, there are three abstract methods declared: one of them is for subject
identification, implemented in the subject-dependent subclasses, and the other
twoare auxiliary ones implemented in each individual refactoring detector
class.Moving on to the newly-created subject subclasses, we have two of them, one for
Moving on to the newly-created subject subclasses, we have two of them, one for
method subjects and one for class subjects, shown in Figures 4.14 to 4.17.

35
protected abstract <T> void identifySubjects(T scopeRegion, SystemObject
systemObject);

public abstract JFrame getDetectorFrame(Object[] dataForIdentification);

public abstract boolean opportunitiesFound();

Figure 4.13: Abstract methods declared in RefactoringDetector abstract superclass

public abstract class RefactoringDetectorClassSubject extends RefactoringDetector {

protected ArrayList<ClassObject> subjectList;

public RefactoringDetectorClassSubject(String title)

super(title);

listOfValidScopes.add(scopeType.PACKAGE_FRAGMENT_ROOT);

listOfValidScopes.add(scopeType.PACKAGE_FRAGMENT);

listOfValidScopes.add(scopeType.COMPILATION_UNIT);

listOfValidScopes.add(scopeType.TYPE);

listOfValidScopes.add(scopeType.NONE);

Figure 4.14: Declaration, fields, and constructor of RefactoringDetectorClassSubject

As you can see, we populate the listOfValidScopes in the constructor, omitting the
invalid Method scope tier. Figure 4.15 contains the code for subject identification,
using the generic scope object that will be returned from identifyScope and casting
it to the appropriate class.

36
protected<T> void identifySubjects(T scopeRegion, SystemObject systemObject)

subjectList = new ArrayList<ClassObject>();

if (scope != scopeType.NONE)

if (scope == scopeType.PACKAGE_FRAGMENT_ROOT){
subjectList.addAll(systemObject.getClassObjects((IPackageFragmentRoo
t) scopeRegion));

else if (scope == scopeType.PACKAGE_FRAGMENT){

subjectList.addAll(systemObject.getClassObjects((IPackageFragment)
scopeRegion));

else if (scope == scopeType.COMPILATION_UNIT){

subjectList.addAll(systemObject.getClassObjects((ICompilationUnit)
scopeRegion));

else if (scope == scopeType.TYPE){


subjectList.addAll(systemObject.getClassObjects((IType)
scopeRegion));

else

subjectList.addAll(systemObject.getClassObjects());

Figure 4.15: Subject identification method for Class Subjects

37
Similarly for the Method Subjects, Figure 4.16 contains the fields and constructor,
while 4.17 and 4.18 contain the subject identification.

public abstract class RefactoringDetectorMethodSubject extends RefactoringDetector


{

protected ArrayList<AbstractMethodDeclaration> subjectList;

public RefactoringDetectorMethodSubject(String title)

super(title);

listOfValidScopes.add(scopeType.PACKAGE_FRAGMENT_ROOT);

listOfValidScopes.add(scopeType.PACKAGE_FRAGMENT);

listOfValidScopes.add(scopeType.COMPILATION_UNIT);

listOfValidScopes.add(scopeType.TYPE);

listOfValidScopes.add(scopeType.METHOD);

listOfValidScopes.add(scopeType.NONE);

Figure 4.16: Declaration, fields, and constructor of RefactoringDetectorMethodSubject

38
protected<T> void identifySubjects(T scopeRegion, SystemObject systemObject)

Set<ClassObject> classObjectsToBeExamined = new LinkedHashSet<ClassObject>();


subjectList = new ArrayList<AbstractMethodDeclaration>();

if (scope != scopeType.NONE){

if (scope == scopeType.PACKAGE_FRAGMENT_ROOT){
classObjectsToBeExamined.addAll(systemObject.getClassObjects((IPackage
FragmentRoot) scopeRegion));

else if (scope == scopeType.PACKAGE_FRAGMENT){


classObjectsToBeExamined.addAll(systemObject.getClassObjects((IPackage
Fragment) scopeRegion));

else if (scope == scopeType.COMPILATION_UNIT){


classObjectsToBeExamined.addAll(systemObject.getClassObjects((ICompila
tionUnit) scopeRegion));

else if (scope == scopeType.TYPE){

classObjectsToBeExamined.addAll(systemObject.getClassObjects((IType)
scopeRegion));

else if (scope == scopeType.METHOD){

AbstractMethodDeclaration methodObject =
systemObject.getMethodObject((IMethod) scopeRegion);

ClassObject declaringClass =
systemObject.getClassObject(methodObject.getClassName());

if(declaringClass != null && !declaringClass.isEnum()

&& !declaringClass.isInterface()

&& methodObject.getMethodBody() != null)

subjectList.add(methodObject);

}
}
else{

classObjectsToBeExamined.addAll(systemObject.getClassObjects());

Figure 4.17: Part 1 of Method Subject identification

39
if(!classObjectsToBeExamined.isEmpty()){

for(ClassObject classObject : classObjectsToBeExamined){

if(!classObject.isEnum() && !classObject.isInterface()) {

ListIterator<MethodObject> methodIterator =
classObject.getMethodIterator();

while(methodIterator.hasNext())

subjectList.add(methodIterator.next());

Figure 4.18: Part 2 of Method Subject identification

Finally, we extract the opportunity detection algorithm in each individual detector


class to its own method, to reduce the overall length of some methods as mentioned
above and to make the code flow more easily understandable. In the end, using the
Inline Method Detector to compare before and after the pattern application, the
code blocks presented in Figures 4.6 and 4.7, in addition to the opportunity
detection code which takes up another 40 lines, is reduced to merely what is shown
in Figure 4.19:

final SystemObject systemObject = ASTReader.getSystemObject();

identifySubjects(identifyScope(), systemObject);

identifyOpportunities(systemObject, threshold);

Figure 4.19

40
For a more quantitative analysis, Table 4.1 containsa full breakdown of number of
lines before and after application of the pattern in each of the individual detector
classes, as well as the numerical and percentage reduction of lines of code in those
classes.

Lines Lines # %
Class
Before After Reduction Reduction

ExtractClassIdentification 307 284 23 7.49

ExtractMethodIdentification 393 320 73 18.57

InlineMethodIdentification 286 233 53 18.53

InlineTempIdentification 420 363 57 13.57

IntroduceExplainingVariableIdentification 604 529 75 12.41

IntroduceParameterObjectIdentification 221 167 54 24.43

MoveMethodIdentification 247 205 42 17

RemoveAssignmentsToParametersIdentification 416 369 47 11.29

ReplaceMethodWithMethodObjectIdentification 293 235 58 19.79

ReplaceTempWithQueryIdentification 429 362 67 15.61

SplitTemporaryVariableIdentification 425 358 67 15.76

Total 4041 3425 616 15.24

Average 367 311 56 15.86

RefactoringDetector 15 101

RefactoringDetectorClassSubject - 57

RefactoringDetectorMethodSubject - 86

Overall, we have a not-insignificant reduction of approximately 15% in total lines of


code in the individual detectors, not to mention the vastly improved extension
capability provided by the pattern.

41
Chapter 5. Conclusions

The problem this thesis deals with relates to the issue of refactoring tool
extensibility. It has been highlighted in several studies regarding refactoring tools,
but, to the best of our knowledge, not much research has been carried out regarding
it. Our solution uses groundwork laid by the tool Refactoring TripAdvisor. We
improved and refined the ideas presented in the original work and subsequent
publications, and formalized them in the form of a pattern for refactoring detectors.
From our study, this pattern is applicable to any of the 68 refactorings devised by
Martin Fowler, and can be further applied to new ones without issue. It is also
language-independent, concerning object-oriented languages of course.

To illustrate its practical value, we applied the pattern in the detectors of


Refactoring TripAdvisor, and showcased the increase in code quality, present in
multiple forms: Firstly, large amounts of duplicated code were removed. Secondly,
methods were better organized and easier to understand. Thirdly, with the addition
of a better class hierarchy, extensibility was greatly increased.

42
Chapter 6. References

[A77] Christopher Alexander. A Pattern Language: Towns, Buildings,


Construction. Oxford University Press, 1977

[BC87] Kent Beck, Ward Cunningham. Using Pattern Languages for Object–
Oriented Programs. OOPSLA 87 workshop on Specification and Design
for Object–Oriented Programming, 1987

[F99] Martin Fowler. Refactoring: Improving the Design of Existing Code.


Addison Wesley, 1999

[FTSC12] Marios Fokaefs, Nikolaos Tsantalis, Eleni Stroulia, Alexander


Chatzigeorgiou. Identification and application of Extract Class
refactorings in object–oriented systems. The Journal of Systems and
Software, 85, 2012

[GOF94] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design
Patterns: Elements of Reusable Object–Oriented Software. Addison
Wesley, 1994

[H04] Neil Harrison. Advanced Pattern Writing. Proceedings of European


Conference on Pattern Languages of Programs, 2003

[L02] Antonio Leitao. A Formal Pattern Language for Refactoring of Lisp


Programs. Euromicro Conference on Software Maintenance and
Reengineering, 2002

43
[LT12] Huiqing Li, Simon Thompson. Let’s Make Refactoring Tools user–
Extensible! Fifth Workshop on Refactoring Tools, 2012

[M03] Tom Mens. Refactoring: Current Research and Future Trends.


Electronic Notes on Computer Science, 82(3), 2003

[MD97] Gerard Meszaros, Jim Doble. Pattern Languages of Program Design.


Addison Wesley, 1997

[MHPB11] E. Murphy–Hill, C. Parnin and A. P. Black. How we refactor, and how we


know it. IEEE Transactions on Software Engineering, 38(1), 5–18, 2011.

[O92] William Opdyke. Refactoring Object–Oriented Frameworks, Ph.D. Thesis.


University of Illinois a Urbana–Champaign, 1992

[TC09] Nikolaos Tsantalis, Alexander Chatzigeorgiou. Identification of Extract


Method Refactoring Opportunities. In Proc. 13th European Conference
on Software Maintenance and Reengineering, 2009

[TC14] Nikolaos Tsantalis, Alexander Chatzigeorgiou. JDeodorant Eclipse plugin


found at https://users.encs.concordia.ca/~nikolaos/jdeodorant

[V16] Theofanis Vartziotis. Refactoring TripAdvisor, Master's Thesis.


University of Ioannina, 2016

[VZV15] Theofanis Vartziotis, Apostolos Zarras, Panos Vassiliadis. Never Travel


Without a Map: Recommending Trips in the Archipelago of Refactorings.
In Proc. 10th Joint Meeting on Foundations of Software Engineering, pp.
922–925, 2015

[WF11] Tim Wellhausen, Andreas Fießer. How to write a pattern? – A rough


guide for first–time pattern authors. Proceedings of European
Conference on Pattern Languages of Programs, 2011

44

S-ar putea să vă placă și