This is a showcase review for our sponsors at CodeProject. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.
Introduction
A favorite interview question of mine to ask a candidate for a developer position has always been, "What do you not like about the programming language you use?" A good candidate responds with understanding about his or her workflow and efficiency, how a language promotes or hinders the ability to create solid code, and (perhaps more importantly) to maintain that code as it grows.
For me, the answer comes without hesitation: my pet peeve with most modern languages is the tedium you must endure as you code; the mindless little tasks that chip away at the time I should spend creating actual design and functionality. You know the sort: updating the declaration when you change a definition, renaming a variable or function name everywhere it's used, breaking a large function into smaller ones - why are we still performing these menial tasks? It's 2006! I may not have my flying car but the least I can ask for is the machine whirring away at three billion clicks per second to lend a hand while I'm typing text into it.
And so it should be easy to imagine the enthusiasm with which I met the introduction of code refactoring into the latest version of Visual Assist X - the add-in from Whole Tomato Software that works in just about every Microsoft IDE. At last, someone has put my processor to good use while I'm writing code, with a complex parsing engine running behind the scenes, seamlessly examining my code and turning what were once tedious and bug-prone chores into something automatic. Let's take a look at the refactorings available for C/C++ and C#. (My examples are in C++, but refactoring works in C# too.)
Keeping Code Clean
What would you say is the biggest obstacle to clean code? One of the top offenders would definitely be bloated, monolithic functions, hundreds of lines long, and nearly impossible to decipher. Refactoring these types of functions into a proper hierarchy is a task which is time consuming and so frustrating, many developers succumb to the temptation of adding 'just one more line' and letting the next person deal with it.
Extract Method
The Extract Method tool makes breaking apart these large functions virtually painless - simply select the block you want to extract, give it a name, hit the button, and you're done: the new function is implemented, the definition is in the header, and the block is removed from the original function and replaced with a call to your new function. What's more, any local variables that were declared outside the block and used within it are passed as parameters! The first time I saw this, it was like a magic trick. Let's look at an example.
Here's a segment of code from a function that does a variety of things, and is ripe for breaking into smaller pieces:
If we wanted to move all the processing of the data into a separate function, we can do so easily by selecting the text, hovering over it, and using the context-sensitive dropdown box that Visual Assist X pops up.
Selecting this (or just using the hot key) brings up a dialog where you select the name, and as you can see, Visual Assist X has done the rest. The signature is already written, with the needed variables passed in (preserving const, and passing by pointer when they are larger types).
Click 'OK' and the function is created:
... and a call to this new function is added in its old home:
There's something very satisfying about making only the broad strokes of coding and letting the machine do the rest, just select it, pop it out to its own function, and repeat. It's like having a lackey to do your dirty work. The feature is even flexible enough to pull changes out of single lines - let's take the comparison out of the for
-loop and put it in its own function.
Notice how it identifies what the return type should be, based on how it's used:
... and automatically plugs it in correctly:
With this tool, large blocks of code can be restructured into multi-layer hierarchies in seconds, leaving few excuses for not creating clean, encapsulated functions, and maintaining that quality as they grow.
If you're like me, though, you probably approach the idea of a machine manipulating your code with a degree of skepticism; is it really going to properly identify everything? Of course, the machine can't read your mind, but in the vast majority of cases, it identifies the signature of the new function just as I intended (while certainly creating bugs less frequently in the process than I would do it manually!) If you give bad inputs to the feature or it doesn't do exactly what you expect, the entire thing can be easily un-done (both the CPP and header will be restored with one Ctrl-Z), and you can navigate forward and backwards to visually review the changes it made with the navigation commands.
You may have noticed, these extracted functions were all created in the header - this can be easily rectified by Visual Assist X, which leads us into the next set of features it provides (which I would say are my favorite of the refactoring tools) for manipulating definitions and declarations. (Of course, the refactorings related to headers don't apply to C#.)
Keeping your Code in Sync
One of the simplest ways to work more efficiently is to reduce the time you spend repeating tasks. Unfortunately, C++ (and most modern languages) require you to keep various parts of your code in a matching state - definitions must always match the declaration made in a separate file, members must be encapsulated with 'get' and 'set' functions, etc.
These requirements give the languages considerable power from a technical standpoint, but the result for the developer is that he or she is writing the same text repeatedly. Besides being a waste of time, it's one of the easiest ways for bugs to be introduced. The creators behind the refactoring tools in Visual Assist X obviously realize these issues, and have put considerable thought into how best to streamline this decades-old workflow.
Move Implementation to Source File
This feature is a favorite for making short work of my personal code pet-peeve: unnecessarily in-lined functions (defined in the header, only because the original author was too lazy to put it into the CPP):
Both the header and CPP are fixed up with this one command:
This one is a perfect fit for an easy hotkey, since Visual Assist X puts generated functions (created by the Extract Method and other features) into the header by default, leaving you to move them to the CPP if desired.
Create Implementation and Create Declaration
If you're not just moving an existing function but creating a brand new one, here is another tool for you. Whether you like to create your functions in the header and then copy them to the CPP or vice versa, Visual Assist X has you covered, and fills in whichever one is lacking by using the appropriate command:
Encapsulate Field
Another common task that's a staple of clean code is encapsulating a member variable with "Get" and "Set" blocks. Visual Assist X has an answer for this one too:
One especially nice touch is that the text it creates for the function names is customizable using their Autotext feature:
Here, I've changed the names of the functions it creates during an Encapsulate Field call to use the Get and Set standard that I'm used to. The system is flexible enough that you could add more lines here to do some custom thing your app needs (adding a "RegisterForScript
" function which acts on the variable, for example). It's like an auto-replace on steroids - it feeds you the symbol names, and lets you arrange them as you like.
AutoText is a great feature in itself which is integrated quite well with the rest of their tools, and allows you to create common blocks of code quickly and to the exact style you want (language-specific, too).
Add Member and Add Similar Member
After using Visual Assist X, you start to realize how more efficient you can be when you don't have to leave your file and location to put declaration code somewhere else. Say, I'm writing a function, and decide a class needs another member variable or function - Visual Assist X lets me add it without leaving what I'm doing.
A stub is now created in the header, allowing me to start filling it in immediately, or navigate back and continue where I was:
If you have your cursor over an existing method or member variable, you get the command "Add Similar Member" instead, which does the exact same thing as Add Member, but is streamlined by starting with the member signature of whatever is under your cursor:
It's the little additions like this that really makes the whole Visual Assist X interface feel polished and tuned to the way you work, instead of forcing you to change your style of creating code to the way it works.
Keeping your Code Understandable by Others (and Yourself)
As anyone who's worked on a large project knows, code is never completed the first time it's written; rather, it's like a living entity in that it must change and adapt as more features are added and more or different demands placed on it. As this inevitably occurs, it is often also sadly inevitable that the names and documentation fall out-of-sync with what the code actually does, usually due to the cumbersome task of performing these changes being just annoying enough to neglect. The following features automate the process of keeping your function documentation and symbol names (which are themselves a form of documentation) up-to-date.
Rename
Virtually any kind of symbol can be renamed by Visual Assist X: methods, fields, classes, variables, and method parameters. The Rename can also be started on any instance of the symbol, not just on the declaration:
Here, we rename the Data
class to a more descriptive name.
Before changing any code, Visual Assist X brings up a dialog with all the references to the symbol it detected, allowing you to confirm all changes before they happen. And, should you decide against it later, the entire operation can be undone with one invocation of Undo (it fixes all files that were affected).
Change Signature
A similar feature allows you to rename the various identifiers associated with a function:
Invoking this on a function changes the parameter types and function and parameter names, and automatically updates them throughout the function as it is used. Here, we rename the members of CompareStatus
to be more descriptive, and the places within the function where they are used are updated as well.
You can also change the types of parameters, return types, and function qualifiers (like const
), but you will have to update those changes manually.
Find References
The Find References tool is essentially a smarter Find in Files - it looks up the symbol you referenced, and outputs all the locations it finds in a context-based search (so it won't pick up other symbols with the same name).
It puts the results in a window you can dock like any other IDE window:
The icons in the above window are especially helpful - red arrows designate places where the symbol is assigned, green where it is referenced, and the blue block is where it is defined (great for quickly scanning through when you're looking for code that changes a given variable, for example). If there are multiple entries on a single line, they will be listed multiple times, with a "-->" denoting the location within the line. There's also a 'highlight all' feature, which highlights the symbols in your source windows using different colors for assigned symbols:
This tool brings context sensitivity and deeper analysis than Find in Files, and it's faster too.
Document Method
Perhaps the most important aspect of clean code is clear, consistent documentation. There's a handy tool for helping to automate this process which you can invoke from the function definition:
This feature will create a standardized comment block above your function, using specific context-specific data about the class and function you are documenting:
Like all the features where Visual Assist X generates code for you, the style can be customized in their Autotext tool:
The various data Visual Assist X identifies from the context is exposed as special strings which you can enter here (more are available as listed in their documentation as well).
A New Approach to Coding
I've been using Visual Assist X for years, and their code refactoring tools I've described here are just one part of a massive suite of tools integrating with all versions of Microsoft IDEs in the last several years. Alone, those features are enough to make the product valuable, and they cover a wide range of ways to enhance the way you reference, view, and navigate your code. The refactoring tools, however, represent a new direction - at last there are tools that create code - that's a big step, and certainly not an easy one when you consider the number of languages and environments they support, and on top of that, adapting to the different coding styles individuals use within each of those; it is not a small problem.
The creators of Visual Assist X have focused on a core set of tasks in the programming workflow, with really solid solutions under a wide variety of coding styles, and they've done this very well - the most striking thing overall about the product is how seamless the integration is and how polished all the features perform, in every case, in every environment and language. Which is fortunate - I wouldn't accept anything less from an application that is going to be writing code for me.
There's still room to grow, of course, I can think of more things that could be automated, and more ways you can customize what gets generated, and then there is the pot-of-gold at the end of the rainbow: can they give their tool the smarts to do all these refactorings automatically, in the background, while I write? Perhaps, it's on the horizon. But let's not get ahead of ourselves, one giant leap at a time, and I've been quite impressed with this one.
Learn more about what Visual Assist X can do for you.