]>
Commit | Line | Data |
---|---|---|
7c28933b | 1 | \documentclass[USenglish]{ifimaster} |
571ef294 | 2 | \usepackage{import} |
7c28933b | 3 | \usepackage[utf8]{inputenc} |
9ff90080 EK |
4 | \usepackage[T1]{fontenc,url} |
5 | \urlstyle{sf} | |
571ef294 | 6 | \usepackage{babel,textcomp,csquotes,ifimasterforside,varioref,graphicx} |
84fe308b | 7 | \usepackage[style=numeric-comp,backend=bibtex]{biblatex} |
571ef294 | 8 | %\usepackage[backend=biber,style=numeric-comp]{biblatex} |
12c254af | 9 | \usepackage{amsthm} |
00aa0588 | 10 | \usepackage{todonotes} |
8fae7b44 EK |
11 | \usepackage{perpage} %the perpage package |
12 | \MakePerPage{footnote} %the perpage package command | |
9ff90080 | 13 | |
12c254af EK |
14 | \theoremstyle{plain} |
15 | \newtheorem*{wordDef}{Definition} | |
16 | ||
17 | \newcommand{\definition}[1]{\begin{wordDef}#1\end{wordDef}} | |
137e0e7b EK |
18 | \newcommand{\see}[1]{(see \ref{#1})} |
19 | \newcommand{\explanation}[3]{\noindent\textbf{\textit{#1}}\\*\emph{When:} | |
20 | #2\\*\emph{How:} #3\\*[-7px]} | |
f041551b EK |
21 | \newcommand{\type}[1]{\texttt{#1}} |
22 | \newcommand{\typeref}[1]{\footnote{\type{#1}}} | |
23 | \newcommand{\typewithref}[2]{\type{#2}\typeref{#1.#2}} | |
24 | \newcommand{\method}[1]{\type{#1}} | |
25 | \newcommand{\methodref}[2]{\footnote{\type{#1}\method{\##2()}}} | |
26 | \newcommand{\methodwithref}[2]{\method{#2}\footnote{\type{#1}\method{\##2()}}} | |
9ff90080 | 27 | |
7c28933b EK |
28 | |
29 | \title{Refactoring} | |
84fe308b | 30 | \subtitle{An unfinished essay} |
7c28933b EK |
31 | \author{Erlend Kristiansen} |
32 | ||
33 | \bibliography{bibliography/master-thesis-erlenkr-bibliography} | |
9ff90080 EK |
34 | |
35 | \begin{document} | |
531c4132 | 36 | \ififorside |
9ff90080 | 37 | \frontmatter{} |
9ff90080 EK |
38 | |
39 | ||
40 | \chapter*{Abstract} | |
41 | Empty document. | |
42 | ||
43 | \tableofcontents{} | |
44 | \listoffigures{} | |
45 | \listoftables{} | |
46 | ||
47 | \chapter*{Preface} | |
48 | ||
055dca93 | 49 | \mainmatter |
00aa0588 | 50 | |
b0e80574 | 51 | \chapter{Introduction} |
7c28933b | 52 | |
8fae7b44 | 53 | \section{What is Refactoring?} |
7c28933b | 54 | |
00aa0588 | 55 | This question is best answered dividing the answer into two parts. First |
8fae7b44 | 56 | defining the concept of a refactoring, then discuss what the discipline of |
51a854d4 | 57 | refactoring is all about. And to make it clear already from the beginning: The |
8fae7b44 | 58 | discussions in this report must be seen in the context of object oriented |
51a854d4 EK |
59 | programming languages. It may be obvious, but much of the material will not make |
60 | much sense otherwise, although some of the techniques may be applicable to | |
00aa0588 EK |
61 | sequential \todo{sequential?} languages, then possibly in other forms. |
62 | ||
63 | \subsection{Defining refactoring} | |
8fae7b44 | 64 | Martin Fowler, in his masterpiece on refactoring \cite{refactoring}, defines a |
00aa0588 EK |
65 | refactoring like this: |
66 | \begin{quote} | |
67 | \emph{Refactoring} (noun): a change made to the \todo{what does he mean by | |
68 | internal?} internal structure of software to make it easier to understand and | |
69 | cheaper to modify without changing its observable | |
8fae7b44 | 70 | behavior.~\cite{refactoring} % page 53 |
00aa0588 | 71 | \end{quote} |
137e0e7b | 72 | This definition gives additional meaning to the word \emph{refactoring}, beyond |
00aa0588 EK |
73 | its \todo{original?} original meaning. Fowler is mixing the \emph{motivation} |
74 | behind refactoring into his definition. Instead it could be made clean, only | |
8fae7b44 | 75 | considering the mechanical and behavioral aspects of refactoring. That is to |
137e0e7b | 76 | factor the program again, putting it together in a different way than before, |
8fae7b44 | 77 | while preserving the behavior of the program. An alternative definition could |
137e0e7b | 78 | then be: |
51a854d4 | 79 | |
51a854d4 | 80 | \definition{A refactoring is a transformation |
8fae7b44 | 81 | done to a program without altering its external behavior.} |
00aa0588 | 82 | |
8fae7b44 EK |
83 | So a refactoring primarily changes how the \emph{code} of a program is perceived |
84 | by the \emph{programmer}, and not the behavior experienced by any user of the | |
137e0e7b | 85 | program. Although the logical meaning is preserved, such changes could |
8fae7b44 | 86 | potentially alter the program's behavior when it comes to performance gain or |
00aa0588 EK |
87 | penalties. So any logic depending on the performance of a program could make the |
88 | program behave differently after a refactoring. | |
89 | ||
137e0e7b EK |
90 | In the extreme case one could argue that such a thing as \emph{software |
91 | obfuscation} is to refactor. If we where to define it as a refactoring, it could | |
92 | be defined as a composite refactoring \see{intro_composite}, consisting of, for | |
93 | instance, a series of rename refactorings. (But it could of course be much more | |
94 | complex, and the mechanics of it would not exactly be carved in stone.) To | |
95 | perform some serious obfuscation one would also take advantage of techniques not | |
96 | found among established refactorings, such as removing whitespace. This might | |
97 | not even generate a different syntax tree for languages not sensitive to | |
98 | whitespace, placing it in the gray area of what transformations is to be | |
99 | considered refactorings. | |
00aa0588 EK |
100 | |
101 | Finally, to \emph{refactor} is (quoting Martin Fowler) | |
7c28933b EK |
102 | \begin{quote} |
103 | \ldots to restructure software by applying a series of refactorings without | |
8fae7b44 | 104 | changing its observable behavior.~\cite{refactoring} % page 54, definition |
7c28933b EK |
105 | \end{quote} |
106 | ||
00aa0588 EK |
107 | % subsection with the history of refactoring? |
108 | ||
109 | \subsection{Motivation} % better headline? | |
110 | To get a grasp of what refactoring is all about, we can answer this question: | |
111 | \emph{Why do people refactor?} Possible answers could include: ``To remove | |
112 | duplication'' or ``to break up long methods''. Practitioners of the art of | |
113 | Design Patterns~\cite{dp} could say that they do it to introduce a long-needed | |
114 | pattern to their program's design. So it's safe to say that peoples' intentions | |
115 | are to make their programs \emph{better} in some sense. But what aspects of the | |
116 | programs are becoming improved? | |
51a854d4 EK |
117 | |
118 | As already mentioned, people often refactor to get rid of duplication. Moving | |
00aa0588 EK |
119 | identical or similar code into methods, and maybe pushing those up or down in |
120 | their hierarchies. Making template methods for overlapping algorithms | |
121 | \todo{better?: functionality} and so on. It's all about gathering what belongs | |
122 | together and putting it all in one place. And the result? The code is easier to | |
137e0e7b EK |
123 | maintain. When removing the implicit coupling between the code snippets, the |
124 | location of a bug is limited to only one place, and new functionality need only | |
125 | to be added this one place, instead of a number of places people might not even | |
126 | remember. | |
51a854d4 EK |
127 | |
128 | The same people find out that their program contains a lot of long and | |
129 | hard-to-grasp methods. Then what do they do? They begin dividing their methods | |
00aa0588 EK |
130 | into smaller ones, using the \emph{Extract Method} |
131 | refactoring~\cite{refactoring}. Then they may discover something about their | |
132 | program that they weren't aware of before; revealing bugs they didn't know about | |
133 | or couldn't find due to the complex structure of their program. \todo{Proof?} | |
134 | Making the methods smaller and giving good names to the new ones clarifies the | |
135 | algorithms and enhances the \emph{understandability} of the program. This makes | |
136 | simple refactoring an excellent method for exploring unknown program code, or | |
137 | code that you had forgotten that you wrote! | |
51a854d4 EK |
138 | |
139 | The word \emph{simple} came up in the last section. In fact, most basic | |
140 | refactorings are simple. The true power of them are revealed first when they are | |
137e0e7b | 141 | combined into larger --- higher level --- refactorings, called \emph{composite |
8fae7b44 | 142 | refactorings} \see{intro_composite}. Often the goal of such a series of |
137e0e7b EK |
143 | refactorings is a design pattern. Thus the \emph{design} can be evolved |
144 | throughout the lifetime of a program, opposed to designing up-front. It's all | |
51a854d4 EK |
145 | about being structured and taking small steps to improve the design. |
146 | ||
147 | Many refactorings are aimed at lowering the coupling between different classes | |
148 | and different layers of logic. Say for instance that the coupling between the | |
149 | user interface and the business logic of a program is lowered. Then the business | |
150 | logic of the program could much easier be the target of automated tests, | |
0b0567f2 EK |
151 | increasing the productivity in the software development process. It would also |
152 | be much easier to distribute the different parts of the program if they were | |
153 | decoupled. | |
154 | ||
155 | Another effect of refactoring is that with the increased separation of concerns | |
137e0e7b EK |
156 | coming out of many refactorings, the \emph{performance} is improved. When |
157 | profiling programs, the problem parts are narrowed down to smaller parts of the | |
158 | code, which are easier to tune, and optimization can be performed only where | |
159 | needed and in a more effective way. | |
160 | ||
161 | Refactoring program code --- with a goal in mind --- can give the code itself | |
162 | more value. That is in the form of robustness to bugs, understandability and | |
163 | maintainability. With the first as an obvious advantage, but with the following | |
00aa0588 EK |
164 | two being also very important in software development. By incorporating |
165 | refactoring in the development process, bugs are found faster, new functionality | |
166 | is added more easily and code is easier to understand by the next person exposed | |
167 | to it, which might as well be the person who wrote it. So, refactoring can also | |
137e0e7b | 168 | add to the monetary value of a business, by increased productivity of the |
8fae7b44 | 169 | development process in the long run. Where this last point also should open |
137e0e7b EK |
170 | the eyes of some nearsighted managers who seldom see beyond the next milestone. |
171 | ||
172 | ||
173 | \section{Classification of refactorings} | |
174 | % only interesting refactorings | |
175 | % with 2 detailed examples? One for structured and one for intra-method? | |
176 | % Is replacing Bubblesort with Quick Sort considered a refactoring? | |
177 | ||
178 | \subsection{Structural refactorings} | |
179 | ||
180 | \subsubsection{Basic refactorings} | |
181 | ||
182 | % Composing Methods | |
183 | \explanation{Extract Method}{You have a code fragment that can be grouped | |
184 | together.}{Turn the fragment into a method whose name explains the purpose of | |
185 | the method.} | |
186 | ||
187 | \explanation{Inline Method}{A method's body is just as clear as its name.}{Put | |
188 | the method's body into the body of its callers and remove the method.} | |
189 | ||
190 | \explanation{Inline Temp}{You have a temp that is assigned to once with a simple | |
191 | expression, and the temp is getting in the way of other refactorings.}{Replace | |
192 | all references to that temp with the expression} | |
193 | ||
194 | % Moving Features Between Objects | |
195 | \explanation{Move Method}{A method is, or will be, using or used by more | |
196 | features of another class than the class on which it is defined.}{Create a new | |
197 | method with a similar body in the class it uses most. Either turn the old method | |
198 | into a simple delegation, or remove it altogether.} | |
199 | ||
200 | \explanation{Move Field}{A field is, or will be, used by another class more than | |
201 | the class on which it is defined}{Create a new field in the target class, and | |
202 | change all its users.} | |
203 | ||
204 | % Organizing Data | |
205 | \explanation{Replace Magic Number with Symbolic Constant}{You have a literal | |
206 | number with a particular meaning.}{Create a constant, name it after the meaning, | |
207 | and replace the number with it.} | |
208 | ||
209 | \explanation{Encapsulate Field}{There is a public field.}{Make it private and | |
210 | provide accessors.} | |
211 | ||
212 | \explanation{Replace Type Code with Class}{A class has a numeric type code that | |
8fae7b44 | 213 | does not affect its behavior.}{Replace the number with a new class.} |
137e0e7b EK |
214 | |
215 | \explanation{Replace Type Code with Subclasses}{You have an immutable type code | |
8fae7b44 | 216 | that affects the behavior of a class.}{Replace the type code with subclasses.} |
137e0e7b EK |
217 | |
218 | \explanation{Replace Type Code with State/Strategy}{You have a type code that | |
8fae7b44 | 219 | affects the behavior of a class, but you cannot use subclassing.}{Replace the |
137e0e7b EK |
220 | type code with a state object.} |
221 | ||
222 | % Simplifying Conditional Expressions | |
223 | \explanation{Consolidate Duplicate Conditional Fragments}{The same fragment of | |
8fae7b44 | 224 | code is in all branches of a conditional expression.}{Move it outside of the |
137e0e7b EK |
225 | expression.} |
226 | ||
227 | \explanation{Remove Control Flag}{You have a variable that is acting as a | |
228 | control flag fro a series of boolean expressions.}{Use a break or return | |
229 | instead.} | |
230 | ||
231 | \explanation{Replace Nested Conditional with Guard Clauses}{A method has | |
8fae7b44 | 232 | conditional behavior that does not make clear the normal path of |
137e0e7b EK |
233 | execution.}{Use guard clauses for all special cases.} |
234 | ||
8fae7b44 | 235 | \explanation{Introduce Null Object}{You have repeated checks for a null |
137e0e7b EK |
236 | value.}{Replace the null value with a null object.} |
237 | ||
238 | \explanation{Introduce Assertion}{A section of code assumes something about the | |
239 | state of the program.}{Make the assumption explicit with an assertion.} | |
240 | ||
241 | % Making Method Calls Simpler | |
242 | \explanation{Rename Method}{The name of a method does not reveal its | |
243 | purpose.}{Change the name of the method} | |
244 | ||
245 | \explanation{Add Parameter}{A method needs more information from its | |
246 | caller.}{Add a parameter for an object that can pass on this information.} | |
247 | ||
248 | \explanation{Remove Parameter}{A parameter is no longer used by the method | |
249 | body.}{Remove it.} | |
250 | ||
251 | %\explanation{Parameterize Method}{Several methods do similar things but with | |
252 | %different values contained in the method.}{Create one method that uses a | |
253 | %parameter for the different values.} | |
254 | ||
255 | \explanation{Preserve Whole Object}{You are getting several values from an | |
256 | object and passing these values as parameters in a method call.}{Send the whole | |
257 | object instead.} | |
258 | ||
259 | \explanation{Remove Setting Method}{A field should be set at creation time and | |
260 | never altered.}{Remove any setting method for that field.} | |
261 | ||
262 | \explanation{Hide Method}{A method is not used by any other class.}{Make the | |
263 | method private.} | |
264 | ||
8fae7b44 EK |
265 | \explanation{Replace Constructor with Factory Method}{You want to do more than |
266 | simple construction when you create an object}{Replace the constructor with a | |
137e0e7b EK |
267 | factory method.} |
268 | ||
269 | % Dealing with Generalization | |
8fae7b44 | 270 | \explanation{Pull Up Field}{Two subclasses have the same field.}{Move the field |
137e0e7b EK |
271 | to the superclass.} |
272 | ||
273 | \explanation{Pull Up Method}{You have methods with identical results on | |
274 | subclasses.}{Move them to the superclass.} | |
275 | ||
8fae7b44 | 276 | \explanation{Push Down Method}{Behavior on a superclass is relevant only for |
137e0e7b EK |
277 | some of its subclasses.}{Move it to those subclasses.} |
278 | ||
279 | \explanation{Push Down Field}{A field is used only by some subclasses.}{Move the | |
280 | field to those subclasses} | |
281 | ||
282 | \explanation{Extract Interface}{Several clients use the same subset of a class's | |
8fae7b44 | 283 | interface, or two classes have part of their interfaces in common.}{Extract the |
137e0e7b EK |
284 | subset into an interface.} |
285 | ||
286 | \explanation{Replace Inheritance with Delegation}{A subclass uses only part of a | |
287 | superclasses interface or does not want to inherit data.}{Create a field for the | |
288 | superclass, adjust methods to delegate to the superclass, and remove the | |
289 | subclassing.} | |
290 | ||
291 | \explanation{Replace Delegation with Inheritance}{You're using delegation and | |
292 | are often writing many simple delegations for the entire interface}{Make the | |
293 | delegating class a subclass of the delegate.} | |
294 | ||
295 | \subsubsection{Composite refactorings} | |
296 | ||
297 | % Composing Methods | |
298 | % \explanation{Replace Method with Method Object}{}{} | |
299 | ||
300 | % Moving Features Between Objects | |
301 | \explanation{Extract Class}{You have one class doing work that should be done by | |
302 | two}{Create a new class and move the relevant fields and methods from the old | |
303 | class into the new class.} | |
304 | ||
305 | \explanation{Inline Class}{A class isn't doing very much.}{Move all its features | |
306 | into another class and delete it.} | |
307 | ||
308 | \explanation{Hide Delegate}{A client is calling a delegate class of an | |
309 | object.}{Create Methods on the server to hide the delegate.} | |
310 | ||
311 | \explanation{Remove Middle Man}{A class is doing to much simple delegation.}{Get | |
312 | the client to call the delegate directly.} | |
313 | ||
314 | % Organizing Data | |
315 | \explanation{Replace Data Value with Object}{You have a data item that needs | |
8fae7b44 | 316 | additional data or behavior.}{Turn the data item into an object.} |
137e0e7b EK |
317 | |
318 | \explanation{Change Value to Reference}{You have a class with many equal | |
319 | instances that you want to replace with a single object.}{Turn the object into a | |
320 | reference object.} | |
321 | ||
322 | \explanation{Encapsulate Collection}{A method returns a collection}{Make it | |
8fae7b44 | 323 | return a read-only view and provide add/remove methods.} |
137e0e7b EK |
324 | |
325 | % \explanation{Replace Array with Object}{}{} | |
326 | ||
327 | \explanation{Replace Subclass with Fields}{You have subclasses that vary only in | |
328 | methods that return constant data.}{Change the methods to superclass fields and | |
329 | eliminate the subclasses.} | |
330 | ||
331 | % Simplifying Conditional Expressions | |
332 | \explanation{Decompose Conditional}{You have a complicated conditional | |
333 | (if-then-else) statement.}{Extract methods from the condition, then part, an | |
334 | else part.} | |
335 | ||
336 | \explanation{Consolidate Conditional Expression}{You have a sequence of | |
337 | conditional tests with the same result.}{Combine them into a single conditional | |
338 | expression and extract it.} | |
339 | ||
340 | \explanation{Replace Conditional with Polymorphism}{You have a conditional that | |
8fae7b44 | 341 | chooses different behavior depending on the type of an object.}{Move each leg |
137e0e7b EK |
342 | of the conditional to an overriding method in a subclass. Make the original |
343 | method abstract.} | |
344 | ||
345 | % Making Method Calls Simpler | |
346 | \explanation{Replace Parameter with Method}{An object invokes a method, then | |
347 | passes the result as a parameter for a method. The receiver can also invoke this | |
348 | method.}{Remove the parameter and let the receiver invoke the method.} | |
349 | ||
350 | \explanation{Introduce Parameter Object}{You have a group of parameters that | |
351 | naturally go together.}{Replace them with an object.} | |
352 | ||
353 | % Dealing with Generalization | |
354 | \explanation{Extract Subclass}{A class has features that are used only in some | |
355 | instances.}{Create a subclass for that subset of features.} | |
356 | ||
357 | \explanation{Extract Superclass}{You have two classes with similar | |
358 | features.}{Create a superclass and move the common features to the | |
359 | superclass.} | |
360 | ||
361 | \explanation{Collapse Hierarchy}{A superclass and subclass are not very | |
362 | different.}{Merge them together.} | |
363 | ||
364 | \explanation{Form Template Method}{You have two methods in subclasses that | |
365 | perform similar steps in the same order, yet the steps are different.}{Get the | |
366 | steps into methods with the same signature, so that the original methods become | |
367 | the same. Then you can pull them up.} | |
368 | ||
369 | ||
370 | \subsection{Functional refactorings} | |
371 | ||
372 | \explanation{Substitute Algorithm}{You want to replace an algorithm with one | |
373 | that is clearer.}{Replace the body of the method with the new algorithm.} | |
00aa0588 EK |
374 | |
375 | ||
376 | \section{The impact on software quality} | |
377 | ||
378 | \subsection{What is meant by quality?} | |
379 | The term \emph{software quality} has many meanings. It all depends on the | |
9a55a5bc EK |
380 | context we put it in. If we look at it with the eyes of a software developer, it |
381 | usually mean that the software is easily maintainable and testable, or in other | |
382 | words, that it is \emph{well designed}. This often correlates with the | |
383 | management scale, where \emph{keeping the schedule} and \emph{customer | |
137e0e7b EK |
384 | satisfaction} is at the center. From the customers point of view, in addition to |
385 | good usability, \emph{performance} and \emph{lack of bugs} is always | |
386 | appreciated, measurements that are also shared by the software developer. (In | |
387 | addition, such things as good documentation could be measured, but this is out | |
388 | of the scope of this document.) | |
9a55a5bc | 389 | |
00aa0588 | 390 | \subsection{The impact on performance} |
9a55a5bc EK |
391 | \begin{quote} |
392 | Refactoring certainly will make software go more slowly, but it also makes the | |
00aa0588 | 393 | software more amenable to performance tuning.~\cite{refactoring} % page 69 |
9a55a5bc EK |
394 | \end{quote} |
395 | There is a common belief that refactoring compromises performance, due to | |
396 | increased degree of indirection and that polymorphism is slower than | |
397 | conditionals. | |
398 | ||
00aa0588 | 399 | In a survey, Demeyer~\cite{demeyer2002} disproves this view in the case of |
9a55a5bc EK |
400 | polymorphism. He is doing an experiment on, what he calls, ``Transform Self Type |
401 | Checks'' where you introduce a new polymorphic method and a new class hierarchy | |
402 | to get rid of a class' type checking of a ``type attribute``. He uses this kind | |
403 | of transformation to represent other ways of replacing conditionals with | |
404 | polymorphism as well. The experiment is performed on the C++ programming | |
00aa0588 EK |
405 | language and with three different compilers and platforms. \todo{But is the |
406 | result better?} Demeyer concludes that, with compiler optimization turned on, | |
407 | polymorphism beats middle to large sized if-statements and does as well as | |
408 | case-statements. (In accordance with his hypothesis, due to similarities | |
409 | between the way C++ handles polymorphism and case-statements.) | |
9a55a5bc EK |
410 | \begin{quote} |
411 | The interesting thing about performance is that if you analyze most programs, | |
412 | you find that they waste most of their time in a small fraction of the code. | |
00aa0588 | 413 | ~\cite{refactoring} |
9a55a5bc EK |
414 | \end{quote} |
415 | So, although an increased amount of method calls could potentially slow down | |
416 | programs, one should avoid premature optimization and sacrificing good design, | |
417 | leaving the performance tuning until after profiling the software and having | |
418 | isolated the actual problem areas. | |
419 | ||
420 | ||
00aa0588 | 421 | |
00aa0588 EK |
422 | \section{Correctness of refactorings} |
423 | % Volker's example? | |
424 | ||
425 | \section{Composite refactorings} \label{intro_composite} | |
426 | % motivation, example(s) | |
427 | % manual vs automated? | |
428 | % what about refactoring in a very large code base? | |
429 | ||
430 | \section{Software metrics} | |
431 | ||
432 | ||
433 | %\part{The project} | |
434 | %\chapter{Planning the project} | |
435 | %\part{Conclusion} | |
436 | %\chapter{Results} | |
437 | ||
b0e80574 | 438 | |
5837a41f EK |
439 | \chapter{Refactorings in Eclipse JDT: Design, Shortcomings and Wishful |
440 | Thinking}\label{ch:jdt_refactorings} | |
441 | ||
442 | This chapter will deal with some of the design behind refactoring support in | |
443 | Eclipse, and the JDT in specific. After which it will follow a section about | |
444 | shortcomings of the refactoring API in terms of composition of refactorings. The | |
445 | chapter will be concluded with a section telling some of the ways the | |
446 | implementation of refactorings in the JDT could have worked to facilitate | |
447 | composition of refactorings. | |
055dca93 | 448 | |
b0e80574 | 449 | \section{Design} |
f041551b EK |
450 | The refactoring world of Eclipse can in general be separated into two parts: The |
451 | language independent part and the the part written for a specific programming | |
07e173d4 EK |
452 | language -- the language that is the target of the supported refactorings. |
453 | \todo{What about the language specific part?} | |
f041551b EK |
454 | |
455 | \subsection{The Language Toolkit} | |
456 | The Language Toolkit, or LTK for short, is the framework that is used to | |
457 | implement refactorings in Eclipse. It is language independent and provides the | |
458 | abstractions of a refactoring and the change it generates, in the form of the | |
459 | classes \typewithref{org.eclipse.ltk.core.refactoring}{Refactoring} and | |
460 | \typewithref{org.eclipse.ltk.core.refactoring}{Change}. (There is also parts of | |
461 | the LTK that is concerned with user interaction, but they will not be discussed | |
462 | here, since they are of little value to us and our use of the framework.) | |
463 | ||
464 | \subsubsection{The Refactoring Class} | |
465 | The abstract class \type{Refactoring} is the core of the LTK framework. Every | |
466 | refactoring that is going to be supported by the LTK have to end up creating an | |
467 | instance of one of its subclasses. The main responsibilities of subclasses of | |
468 | \type{Refactoring} is to implement template methods for condition checking | |
469 | (\methodwithref{org.eclipse.ltk.core.refactoring.Refactoring}{checkInitialConditions} | |
470 | and | |
471 | \methodwithref{org.eclipse.ltk.core.refactoring.Refactoring}{checkFinalConditions}), | |
472 | in addition to the | |
473 | \methodwithref{org.eclipse.ltk.core.refactoring.Refactoring}{createChange} | |
07e173d4 EK |
474 | method that creates and returns an instance of the \type{Change} class. |
475 | ||
476 | If the refactoring shall support that others participate in it when it is | |
477 | executed, the refactoring has to be a processor-based | |
478 | refactoring\typeref{org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring}. | |
479 | It then delegates to its given | |
480 | \typewithref{org.eclipse.ltk.core.refactoring.participants}{RefactoringProcessor} | |
481 | for condition checking and change creation. | |
f041551b EK |
482 | |
483 | \subsubsection{The Change Class} | |
07e173d4 EK |
484 | This class is the base class for objects that is responsible for performing the |
485 | actual workspace transformations in a refactoring. The main responsibilities for | |
486 | its subclasses is to implement the | |
487 | \methodwithref{org.eclipse.ltk.core.refactoring.Change}{perform} and | |
488 | \methodwithref{org.eclipse.ltk.core.refactoring.Change}{isValid} methods. The | |
489 | \method{isValid} method verifies that the change object is valid and thus can be | |
490 | executed by calling its \method{perform} method. The \method{perform} method | |
491 | performs the desired change and returns an undo change that can be executed to | |
492 | reverse the effect of the transformation done by its originating change object. | |
493 | ||
61420ef7 | 494 | \subsubsection{Executing a Refactoring}\label{executing_refactoring} |
07e173d4 EK |
495 | The life cycle of a refactoring generally follows two steps after creation: |
496 | condition checking and change creation. By letting the refactoring object be | |
497 | handled by a | |
498 | \typewithref{org.eclipse.ltk.core.refactoring}{CheckConditionsOperation} that | |
499 | in turn is handled by a | |
500 | \typewithref{org.eclipse.ltk.core.refactoring}{CreateChangeOperation}, it is | |
501 | assured that the change creation process is managed in a proper manner. | |
502 | ||
503 | The actual execution of a change object has to follow a detailed life cycle. | |
504 | This life cycle is honored if the \type{CreateChangeOperation} is handled by a | |
505 | \typewithref{org.eclipse.ltk.core.refactoring}{PerformChangeOperation}. If also | |
506 | an undo manager\typeref{org.eclipse.ltk.core.refactoring.IUndoManager} is set | |
507 | for the \type{PerformChangeOperation}, the undo change is added into the undo | |
508 | history. | |
055dca93 | 509 | |
b0e80574 | 510 | \section{Shortcomings} |
80663734 | 511 | This section is introduced naturally with a conclusion: The JDT refactoring |
5837a41f EK |
512 | implementation does not facilitate composition of refactorings. |
513 | \todo{refine}This section will try to explain why, and also identify other | |
514 | shortcomings of both the usability and the readability of the JDT refactoring | |
515 | source code. | |
80663734 EK |
516 | |
517 | I will begin at the end and work my way toward the composition part of this | |
518 | section. | |
519 | ||
5837a41f | 520 | \subsection{Absence of Generics in Eclipse Source Code} |
80663734 EK |
521 | This section is not only concerning the JDT refactoring API, but also large |
522 | quantities of the Eclipse source code. The code shows a striking absence of the | |
523 | Java language feature of generics. It is hard to read a class' interface when | |
5837a41f EK |
524 | methods return objects or takes parameters of raw types such as \type{List} or |
525 | \type{Map}. This sometimes results in having to read a lot of source code to | |
526 | understand what is going on, instead of relying on the available interfaces. In | |
527 | addition, it results in a lot of ugly code, making the use of typecasting more | |
528 | of a rule than an exception. | |
529 | ||
530 | \subsection{Composite Refactorings Will Not Appear as Atomic Actions} | |
531 | ||
532 | \subsubsection{Missing Flexibility from JDT Refactorings} | |
533 | The JDT refactorings are not made with composition of refactorings in mind. When | |
534 | a JDT refactoring is executed, it assumes that all conditions for it to be | |
535 | applied successfully can be found by reading source files that has been | |
536 | persisted to disk. They can only operate on the actual source material, and not | |
537 | (in-memory) copies thereof. This constitutes a major disadvantage when trying to | |
538 | compose refactorings, since if an exception occur in the middle of a sequence of | |
539 | refactorings, it can leave the project in a state where the composite | |
540 | refactoring was executed only partly. It makes it hard to discard the changes | |
541 | done without monitoring and consulting the undo manager, an approach that is not | |
542 | bullet proof. | |
543 | ||
544 | \subsubsection{Broken Undo History} | |
545 | When designing a composed refactoring that is to be performed as a sequence of | |
546 | refactorings, you would like it to appear as a single change to the workspace. | |
547 | This implies that you would also like to be able to undo all the changes done by | |
548 | the refactoring in a single step. This is not the way it appears when a sequence | |
549 | of JDT refactorings is executed. It leaves the undo history filled up with | |
550 | individual undo actions corresponding to every single JDT refactoring in the | |
551 | sequence. This problem is not trivial to handle in Eclipse. (See section | |
552 | \ref{hacking_undo_history}.) | |
553 | ||
554 | \section{Wishful Thinking} | |
80663734 | 555 | |
80663734 | 556 | |
b0e80574 EK |
557 | |
558 | \chapter{Composite Refactorings in Eclipse} | |
559 | ||
560 | \section{A Simple Ad Hoc Model} | |
561 | As pointed out in chapter \ref{ch:jdt_refactorings}, the Eclipse JDT refactoring | |
562 | model is not very well suited for making composite refactorings. Therefore a | |
563 | simple model using changer objects (of type \type{RefaktorChanger}) is used as | |
564 | an abstraction layer on top of the existing Eclipse refactorings. | |
565 | ||
566 | \section{The Extract and Move Method Refactoring} | |
61420ef7 EK |
567 | %The Extract and Move Method Refactoring is implemented mainly using these |
568 | %classes: | |
569 | %\begin{itemize} | |
570 | % \item \type{ExtractAndMoveMethodChanger} | |
571 | % \item \type{ExtractAndMoveMethodPrefixesExtractor} | |
572 | % \item \type{Prefix} | |
573 | % \item \type{PrefixSet} | |
574 | %\end{itemize} | |
575 | ||
576 | \subsection{The Building Blocks} | |
577 | This is a composite refactoring, and hence is built up using several primitive | |
578 | refactorings. These basic building blocks are, as its name implies, the Extract | |
579 | Method Refactoring \cite{refactoring} and the Move Method Refactoring | |
580 | \cite{refactoring}. In Eclipse, the implementations of these refactorings are | |
581 | found in the classes | |
582 | \typewithref{org.eclipse.jdt.internal.corext.refactoring.code}{ExtractMethodRefactoring} | |
583 | and | |
584 | \typewithref{org.eclipse.jdt.internal.corext.refactoring.structure}{MoveInstanceMethodProcessor}, | |
585 | where the last class is designed to be used together with the processor-based | |
586 | \typewithref{org.eclipse.ltk.core.refactoring.participants}{MoveRefactoring}. | |
587 | ||
588 | \subsubsection{The ExtractMethodRefactoring Class} | |
589 | This class is quite simple in its use. The only parameters it requires for | |
590 | construction is a compilation | |
591 | unit\typeref{org.eclipse.jdt.core.ICompilationUnit}, the offset into the source | |
592 | code where the extraction shall start, and the length of the source to be | |
593 | extracted. Then you have to set the method name for the new method together with | |
594 | which access modifier that shall be used and some not so interesting parameters. | |
595 | ||
596 | \subsubsection{The MoveInstanceMethodProcessor Class} | |
597 | For the Move Method the processor requires a little more advanced input than | |
598 | the class for the Extract Method. For construction it requires a method | |
599 | handle\typeref{org.eclipse.jdt.core.IMethod} from the Java Model for the method | |
600 | that is to be moved. Then the target for the move have to be supplied as the | |
601 | variable binding from a chosen variable declaration. In addition to this, one | |
602 | have to set some parameters regarding setters/getters and delegation. | |
603 | ||
604 | To make a whole refactoring from the processor, one have to construct a | |
605 | \type{MoveRefactoring} from it. | |
b0e80574 EK |
606 | |
607 | \subsection{The ExtractAndMoveMethodChanger Class} | |
61420ef7 EK |
608 | The \typewithref{no.uio.ifi.refaktor.changers}{ExtractAndMoveMethodChanger} |
609 | class, that is a subclass of the class | |
610 | \typewithref{no.uio.ifi.refaktor.changers}{RefaktorChanger}, is the class | |
611 | responsible for composing the \type{ExtractMethodRefactoring} and the | |
612 | \type{MoveRefactoring}. Its constructor takes a project | |
613 | handle\typeref{org.eclipse.core.resources.IProject}, the method name for the new | |
614 | method and a \typewithref{no.uio.ifi.refaktor.utils}{SmartTextSelection}. | |
615 | ||
616 | A \type{SmartTextSelection} is basically a text | |
617 | selection\typeref{org.eclipse.jface.text.ITextSelection} object that enforces | |
618 | the providing of the underlying document during creation. I.e. its | |
619 | \methodwithref{no.uio.ifi.refaktor.utils.SmartTextSelection}{getDocument} method | |
620 | will never return \type{null}. | |
621 | ||
622 | Before extracting the new method, the possible targets for the move operation is | |
623 | found with the help of an | |
624 | \typewithref{no.uio.ifi.refaktor.extractors}{ExtractAndMoveMethodPrefixesExtractor}. | |
72b64328 EK |
625 | The possible targets is computed from the prefixes that the extractor returns |
626 | from its | |
61420ef7 EK |
627 | \methodwithref{no.uio.ifi.refaktor.extractors.ExtractAndMoveMethodPrefixesExtractor}{getSafePrefixes} |
628 | method. The changer then choose the most suitable target by finding the most | |
72b64328 EK |
629 | frequent occurring prefix among the safe ones. The target is the type of the |
630 | first part of the prefix. | |
61420ef7 EK |
631 | |
632 | After finding a suitable target, the \type{ExtractAndMoveMethodChanger} first | |
633 | creates an \type{ExtractMethodRefactoring} and performs it as explained in | |
634 | section \ref{executing_refactoring} about the execution of refactorings. Then it | |
635 | creates and performs the \type{MoveRefactoring} in the same way, based on the | |
636 | changes done by the Extract Method refactoring. | |
637 | ||
b0e80574 | 638 | \subsection{The ExtractAndMoveMethodPrefixesExtractor Class} |
61420ef7 | 639 | This extractor extracts properties needed for building the Extract and Move |
72b64328 EK |
640 | Method refactoring. It searches through the given selection to find safe |
641 | prefixes, and those prefixes form a base that can be used to compute possible | |
642 | targets for the move part of the refactoring. It finds both the candidates, in | |
643 | the form of prefixes, and the non-candidates, called unfixes. All prefixes (and | |
644 | unfixes) are represented by a | |
645 | \typewithref{no.uio.ifi.refaktor.extractors}{Prefix}, and they are collected | |
646 | into prefix sets.\typeref{no.uio.ifi.refaktor.extractors.PrefixSet}. | |
647 | ||
648 | The prefixes and unfixes are found by property | |
649 | collectors\typeref{no.uio.ifi.refaktor.extractors.collectors.PropertyCollector}. | |
650 | A property collector follows the visitor pattern \cite{dp} and is of the | |
651 | \typewithref{org.eclipse.jdt.core.dom}{ASTVisitor} type. An \type{ASTVisitor} | |
652 | visits nodes in an abstract syntax tree that forms the Java document object | |
653 | model. The tree consists of nodes of type | |
654 | \typewithref{org.eclipse.jdt.core.do}{ASTNode}. | |
655 | ||
656 | \subsubsection{The PrefixesCollector} | |
657 | The \typewithref{no.uio.ifi.refaktor.extractors.collectors}{PrefixesCollector} | |
658 | is of type \type{PropertyCollector}. It visits expression | |
659 | statements\typeref{org.eclipse.jdt.core.dom.ExpressionStatement} and creates | |
660 | prefixes from its expressions in the case of method invocations. The prefixes | |
661 | found is registered with a prefix set, together with all its sub-prefixes. | |
662 | \todo{Rewrite in the case of changes to the way prefixes are found} | |
663 | ||
664 | \subsubsection{The UnfixesCollector} | |
665 | The \typewithref{no.uio.ifi.refaktor.extractors.collectors}{UnfixesCollector} | |
666 | finds unfixes within the selection. An unfix is a name that is assigned to | |
667 | within the selection. The reason that this cannot be allowed, is that the result | |
668 | would be an assignment to the \type{this} keyword, which is not valid in Java. | |
669 | ||
670 | \subsubsection{Computing Safe Prefixes} | |
671 | A safe prefix is a prefix that does not enclose an unfix. A prefix is enclosing | |
672 | an unfix if the unfix is in the set of its sub-prefixes. As an example, | |
673 | \texttt{``a.b''} is enclosing \texttt{``a''}, as is \texttt{``a''}. The safe | |
674 | prefixes is unified in a \type{PrefixSet} and can be fetched calling the | |
675 | \method{getSafePrefixes} method of the | |
676 | \type{ExtractAndMoveMethodPrefixesExtractor}. | |
61420ef7 | 677 | |
b0e80574 | 678 | \subsection{The Prefix Class} |
72b64328 | 679 | \todo{?} |
b0e80574 EK |
680 | \subsection{The PrefixSet Class} |
681 | ||
5837a41f EK |
682 | \subsection{Hacking the Refactoring Undo |
683 | History}\label{hacking_undo_history} | |
8fae7b44 EK |
684 | \todo{Where to put this section?} |
685 | ||
686 | As an attempt to make multiple subsequent changes to the workspace appear as a | |
687 | single action (i.e. make the undo changes appear as such), I tried to alter | |
688 | the undo changes\typeref{org.eclipse.ltk.core.refactoring.Change} in the history | |
689 | of the refactorings. | |
690 | ||
691 | My first impulse was to remove the, in this case, last two undo changes from the | |
f041551b EK |
692 | undo manager\typeref{org.eclipse.ltk.core.refactoring.IUndoManager} for the |
693 | Eclipse refactorings, and then add them to a composite | |
8fae7b44 EK |
694 | change\typeref{org.eclipse.ltk.core.refactoring.CompositeChange} that could be |
695 | added back to the manager. The interface of the undo manager does not offer a | |
696 | way to remove/pop the last added undo change, so a possible solution could be to | |
697 | decorate \cite{dp} the undo manager, to intercept and collect the undo changes | |
f041551b EK |
698 | before delegating to the \method{addUndo} |
699 | method\methodref{org.eclipse.ltk.core.refactoring.IUndoManager}{addUndo} of the | |
8fae7b44 EK |
700 | manager. Instead of giving it the intended undo change, a null change could be |
701 | given to prevent it from making any changes if run. Then one could let the | |
702 | collected undo changes form a composite change to be added to the manager. | |
703 | ||
704 | There is a technical challenge with this approach, and it relates to the undo | |
705 | manager, and the concrete implementation | |
706 | UndoManager2\typeref{org.eclipse.ltk.internal.core.refactoring.UndoManager2}. | |
707 | This implementation is designed in a way that it is not possible to just add an | |
708 | undo change, you have to do it in the context of an active | |
709 | operation\typeref{org.eclipse.core.commands.operations.TriggeredOperations}. | |
710 | One could imagine that it might be possible to trick the undo manager into | |
711 | believing that you are doing a real change, by executing a refactoring that is | |
712 | returning a kind of null change that is returning our composite change of undo | |
713 | refactorings when it is performed. | |
714 | ||
715 | Apart from the technical problems with this solution, there is a functional | |
716 | problem: If it all had worked out as planned, this would leave the undo history | |
717 | in a dirty state, with multiple empty undo operations corresponding to each of | |
718 | the sequentially executed refactoring operations, followed by a composite undo | |
719 | change corresponding to an empty change of the workspace for rounding of our | |
720 | composite refactoring. The solution to this particular problem could be to | |
721 | intercept the registration of the intermediate changes in the undo manager, and | |
722 | only register the last empty change. | |
723 | ||
724 | Unfortunately, not everything works as desired with this solution. The grouping | |
725 | of the undo changes into the composite change does not make the undo operation | |
726 | appear as an atomic operation. The undo operation is still split up into | |
727 | separate undo actions, corresponding to the change done by its originating | |
728 | refactoring. And in addition, the undo actions has to be performed separate in | |
729 | all the editors involved. This makes it no solution at all, but a step toward | |
730 | something worse. | |
731 | ||
732 | There might be a solution to this problem, but it remains to be found. The | |
733 | design of the refactoring undo management is partly to be blamed for this, as it | |
734 | it is to complex to be easily manipulated. | |
735 | ||
b0e80574 | 736 | |
9ff90080 EK |
737 | \backmatter{} |
738 | \printbibliography | |
055dca93 | 739 | \listoftodos |
9ff90080 | 740 | \end{document} |