is a tool that automates some ReFactoring
s for Smalltalk. See http://st-www.cs.uiuc.edu/users/brant/Refactory/RefactoringBrowser.html
This is absolutely the greatest piece of programming software to come out since the original Smalltalk browser. It completely changes the way you think about programming. All those niggling little "well, I should change this name but..." thoughts go away, because you just change the name because there is always a single menu item to just change the name.
When I started using it, I spent about two hours refactoring at my old pace. I would do a refactoring, then just kind of stare off into space for the five minutes it would have taken me to do the refactoring by hand, then do another, stare into space again. After a while, I caught myself and realized that I had to learn to think BiggerRefactoringThoughts
, and think them faster. Now I use probably half and half refactoring and entering new code, all at the same speed (I should instrument to measure this). -- KentBeck
Discussion about using shell tools to rename symbols moved to ReadWriteGrep
Changing names is the least of its tricks. Some others are:
- ExtractMethod -- make a submethod out of the selected text. If there is already an equivalent method, optionally invoke that instead.
- Inline method -- put the invoked code in place of the invocation. This even works for methods in other classes.
- Move to component -- move the code for a method to another class and invoke it
These three together are extremely powerful. For example, if I notice
area := aRectangle right - aRectangle left * (aRectangle bottom - aRectangle top).
I select the statement to the right of the assignment and "extract method", naming the new method areaOf:. Now I have:
area := self areaOf: aRectangle.
^aRectangle right - aRectangle left * (aRectangle bottom - aRectangle top)
Now I notice that aRectangle cares a lot more about this message than I do, so I "move to component" and choose aRectangle (other possible choices are my instance variables). The type inference stuff generally does a great job of determining the class or classes of aRectangle, or I can type the classes in. I choose "Rectangle", and name the new method "area". Now I have:
area := self areaOf: aRectangle.
^self right - self left * (self bottom - self top)
Now, areaOf: isn't doing me much good, so I go to the original method, select "areaOf:" and "inline method". Now I have:
area := aRectangle area.
^self right - self left * (self bottom - self top)
Now I can imagine extracting "self right - self left" into "width", etc.
The best thing about Refactory is how safe it is. As long as you don't manually edit the source code, you are nearly guaranteed (modulo things like choosing the wrong class for a "move to component") that you won't change the semantics of the program. The more I use it, the more aggressive I am slamming logic around until it makes sense.
Some other cool tricks:
- Add parameter -- add a parameter to every implementor of a message, and to every invocation of the message (with a Default value)
- Remove parameter -- if no implementor of the message uses the parameter, remove it from the methods and the invocations
- Cross referencing from inside the source code -- select any program element in the text and you get a choice of several specialized browsers - senders/implementors of a message, readers/writers of a variable
- Rename -- you can rename classes, variables (all types), and messages
- Abstract/concrete instance variables -- make all references to an instance variable go through a message, or make all references direct
With unlimited undo, you can bravely try experiments that might not pan out.
Using search/replace to perform renaming refactorings ignores the fact that every place the name occurs shouldn't always be renamed (the syntax of the methods). This is especially true for badly named variables. For example, one person might have named a variable "i" instead of a more descriptive name such as "minimalElementIndex". Now if you were to replace "i" with "minimalElementIndex" using search/replace, your program would no longer work since you probably refer to some type such as "minimalElementIndexnt" instead of "int". -- JohnBrant
'' I found myself with a lot of longerfaces once when I did this.
... and if the "i" being replaced above were in a quoted string e.g
String name = String("Brat Simpson");
StrminimalElementIndexng name = StrminimalElementIndexng("Brat SminimalElementIndexntmpson");
You'd at least need to do lexical analysis to work out which lexemes need to be renamed (and apply the correct kind of substitution.)
My understanding with the refactoring browser is that, unlike any script you will write in an editor, it guarantees that the code won't change its meaning. I got this message from one of the browser authors when I was asking why it didn't let me tune the accessor code that it generates when changing from raw instance variables to accessors. His answer was, "well that changes the meaning of the code, so it is not refactoring
, even if it is useful." Ahhh, I thought, and understood something about refactoring. -- AlistairCockburn
Don Roberts, John Brant, and Ralph Johnson, A Refactoring Tool for Smalltalk, "The Theory and Practice of Object Systems", (3) 4, 1997.
How does the RefactoringBrowser
safely refactor without type information? For example, doesn't it need to know what objects are of a particular class in order to rename a method of that class? Does it just guess? -- KaPingYee
It does not just guess. It looks for special cases. Almost always one of the special cases applies. But sometimes it will tell you that it cannot perform a refactoring.
We originally thought that the lack of static type-checking would make it hard to build a refactoring browser for Smalltalk. Lack of type information is a disadvantage, but the advantages of Smalltalk made it a lot easier to make a refactoring browser for Smalltalk than it would have have been for C++ or Java.
Has any work been done on generalizing this, or making a C/C++/Java Refactoring Browser? Naive optimism would lead me to think that this would not be an overwhelming effort. -- Pete Hardie
I think it would be doable for Java. A Refactoring Browser that would work for the whole C++ language, and not some sane subset, would probably be AiComplete. Just think of all the possibilities for creative use of macros and templates. -- StephanHouben
I use emacs and JDE quite a lot for writing Java code. Maybe it would be possible to use EmacsLisp
to write refactoring utilities. I might have a go at it when (if) I have the time. -- BernardMichaelHurley
Actually, there are a few refactoring tools available for Java... see RefactoringBrowserForJava
. However, these tools support only a small portion of the refactorings available in the original RefactoringBrowser
I've never used the RefactoringBrowser
(I don't know SmallTalk
). I am familiar with JRefactory. JRefactory integrates into various IDEs. Is the RefactoringBrowser
a stand-alone tool?
I'm quite new to Smalltalk myself (downloaded VisualWorks just last week), but as I understand it, any implementation of Smalltalk includes a complete development environment in which all code is edited, and the RefactoringBrowser runs inside this environment. There are some interesting technical reasons for this, but I'm afraid I don't understand them well enough to explain at all coherently - perhaps some more experienced Smalltalker could help?
Has there been any work in refactoring browsers for FunctionalProgramming
languages such as CommonLisp
? It seems that the existence of closures would make operations like add/remove parameter quite difficult to perform safely in the general case, and difficult even to be sure of the safety of.
Check out EclipseIde http://www.eclipse.org
, IntellijIdea http://www.intellij.com/idea
Speaking of which, how do the refactoring capabilities of EclipseIde
compare to RefactoringBrowser
If the code-units (routines, methods, classes) were stored in a database, then renaming would be a simple query. GreencoddsTenthRuleOfProgramming
again. Something else to explore: if auto-generated unit ID's were used instead of names by the software-engine, perhaps propagation-based renaming would not be needed. Propagation renaming leans toward a OnceAndOnlyOnce
violation. If you change/rename your primary key often, auto-ID's may be the better approach. See TableOrientedCodeManagement
. -- top
Python has a RefactoringBrowser
Prolog also has a RefactoringBrowser
, called ViPReSS, integrated with the VIM editor.