16 November 2009
A basic knowledge of creating Flex 4 applications is helpful. For more details on creating and customizing Flex 4 applications, see the Flex 4 documentation, the Flex 4 in a week video series and exercises, and the Adobe Flex Developer Center.
Intermediate
Refactoring is the process of cleaning up your code. It is not changing or adding functionality to your application. It is rewriting your code so it is more logical, readable, understandable, and extendable. In most cases, if your application is not broken, you leave it alone; there is no need to invest the time or risk the chance of breaking it. Typically when you do refactoring, then, is when you need to add functionality to an application. You refactor the application first and then add new functionality after. It's easier to restructure code without introducing bugs if you do it in small steps and don't try to change any functionality at the same time and it's easier to add or change functionality when you have clean, refactored code making it simpler and faster to extend and debug the application.
There are many different ways to refactor an application: you can move code from one place to an other, you can create new classes and member of classes; you can replace logic with shorter, more flexible code, you can remove dead code; you can rename classes and its members to more logical names, and so on. Martin Fowler, a well-known expert on refactoring, has catalogued many of these techniques on his website and his book, Refactoring: Improving the Design of Existing Code, the de facto reference on refactoring.
In this tutorial, you are going to refactor a simple Flex drawing application. The beginning application has all its code in the main Application file. The finished application has its code in multiple class files. You start with the more conceptually difficult refactoring tasks of extracting and abstracting code into more reusable, smaller pieces, move to the more straight-forward refactoring tasks of moving and renaming files to make the code more readable and understandable, and finish with the simplest way to clean up your code, by formatting it consistently. The refactoring tasks have been organized into some basic sections: Extract, Abstract, Move, Rename, and Format. These are groupings that I created in order to help give you a framework to introduce the different types of refactorings you can do. Many of the refactoring tasks, however, could be considered to belong to more than one of these groupings. For example, when you extract code from one location, you move it to another. In addition, most people would say formatting your code is not actually refactoring, but since this tutorial is all about cleaning up your code, I am including it as well.
In this section, you import into Flash Builder a Flex project for an application that draws various shapes as you click and move your mouse. You refactor this application in the following sections.



The application works by listening for and responding to mouseDown, mouseMove, and mouseUp events. When the user presses his mouse button, a listener is added for moving the mouse. This onMouseMove() listener uses the graphics API to draw a shape (either a circle, a square, or a star depending on which shape is selected) at the user's mouse position. When the user releases his mouse button, the onMouseMove() listener is removed so no more shapes are drawn. The application has controls in the upper-right corner to allow the user to select different shapes and colors, and to clear all the drawing. Currently, all the code is in one file and some of the variables do not have very descriptive names.
Refactoring by extracting code involves moving code from one location to another. Examples include moving code from one function to another or from one class to another, including to and from superclasses and subclasses. The purpose of extracting code is to minimize duplication of code and maximize code reuse.
Start by moving the code for drawing the star from the onMouseMove() event handler into its own drawStar() function. This will give you the ability to create stars from different locations in your application without having to duplicate or copy and paste code.
drawStar with no arguments and a return type of void.private function drawStar():void{
}
private function drawStar():void{
var size:uint=20;
var xPos:int=mouseX;
var yPos:int=mouseY;
shapeCanvas.graphics.moveTo(xPos+size/2,yPos);
shapeCanvas.graphics.lineTo(xPos+size-size/6,yPos+size);
shapeCanvas.graphics.lineTo(xPos,yPos+size/3);
shapeCanvas.graphics.lineTo(xPos+size,yPos+size/3);
shapeCanvas.graphics.lineTo(xPos+size/6,yPos+size);
shapeCanvas.graphics.lineTo(xPos+size/2,yPos);
}
else if(s=="star"){
drawStar();
}
drawStar() method.The logic to draw the star is now encapsulated in a function so it can be reused by calling the function from multiple places. As written, though, every star created would appear at the same location: at the current position of the mouse. Next, you need to refactor this function so the x and y positions are not hard-coded, but are arguments of the function. This makes the function much more flexible and reusable.
xPos and yPos of type int and give them default values of 0.private function drawStar(xPos:int=0,yPos:int=0):void{
private function drawStar(xPos:int=0,yPos:int=0):void{
var size:uint=20;
shapeCanvas.graphics.moveTo(xPos+size/2,yPos);
shapeCanvas.graphics.lineTo(xPos+size-size/6,yPos+size);
shapeCanvas.graphics.lineTo(xPos,yPos+size/3);
shapeCanvas.graphics.lineTo(xPos+size,yPos+size/3);
shapeCanvas.graphics.lineTo(xPos+size/6,yPos+size);
shapeCanvas.graphics.lineTo(xPos+size/2,yPos);
}
else if(s=="star"){
drawStar(mouseX,mouseY);
}
drawStar() function.init() function, call the drawStar() function and pass to it positions of 10 and 10.private function init():void{
addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
drawStar(10,10);
}
You don't see the star, because you have not set a line style for drawing it. The code for setting the line style is currently in the onMouseDown() event handler. You need it there because you need to set the shape color based on the color selected in the colorPicker before each drawing. Now, however, you also need an initial line style set to draw that first star. You can add a lineStyle() call either in the init() function or inside the drawStar() function. You will add it inside the drawStar() function so that the function can control its own color.
lineStyle() method call from inside the onMouseDown() function and paste it as the second line inside the drawStar() function.private function drawStar(xPos:int=0,yPos:int=0):void{
var size:uint=20;
shapeCanvas.graphics.lineStyle(6, shapeColor, 1, false, "normal", "square", "miter", 8);
shapeCanvas.graphics.moveTo(xPos+size/2,yPos);
...

Right now, you can only specify the position of the star; the color and size are set inside the drawStar() function. To make the function more flexible, further parameterize it so you can specify the color and the size for each star created.
private function drawStar(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000): void{
private function drawStar(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000, size:uint=20):void{
drawStar(10,10,0x0000FF,40);
Inside the else if(s=="star") block of the onMouseMove() function, pass a third argument equal to shapeColor to the drawStar() function. Because you are not passing a fourth argument specifying the size, the default value of 20 pixels will be used.
else if(s=="star"){
drawStar(mouseX,mouseY,shapeColor);
}
Next, move the code for drawing the star into its own class file. This will give you the ability to create stars in this and other applications (although you will only be working with one here) without having to copy and paste code. This also provides encapsulation, the ability to have one object that has its own logic (through properties, methods, and events) for manipulating it.
At this time, leave the Package blank and use the default package. You will move the class into a different package (a different folder) in a later section.


In order to draw graphics using the Graphics API, your class must extend a class that has a graphics property. These classes include the Shape and Sprite classes and their subclasses, one of which is the UIComponent class. You are also going to add instances of your class as visual elements to a Flex container, so the class must also implement the IVisualElement interface. The UIComponent class also implements this interface, so this is the class you want your class to extend.
package
{
import mx.core.UIComponent;
public class Star extends UIComponent
{
public function Star()
{
super();
}
}
}
The function with the same name as the class is called the constructor function. It is a function that is automatically called when you create a new instance of the class. Inside the constructor, super() calls the constructor of the superclass, in this case UIComponent.
drawStar() function.
private function drawStar(xPos:int=0,yPos:int=0, shapeColor:uint=0x000000, size:uint=20):void{
graphics.lineStyle(6, shapeColor, 1, false, "normal", "square", "miter", 8);
graphics.moveTo(xPos+size/2,yPos);
graphics.lineTo(xPos+size-size/6,yPos+size);
graphics.lineTo(xPos,yPos+size/3);
graphics.lineTo(xPos+size,yPos+size/3);
graphics.lineTo(xPos+size/6,yPos+size);
graphics.lineTo(xPos+size/2,yPos);
}
public function Star(xPos:int=0,yPos:int=0, shapeColor:uint=0x000000, size:uint=20)
{
super();
}
private function drawStar():void{
graphics.lineStyle(6, shapeColor, 1, false, "normal", "square", "miter", 8);
...
public function Star(xPos:int=0,yPos:int=0, shapeColor:uint=0x000000, size:uint=20)
{
super();
drawStar();
}
public class Star extends UIComponent
{
private var xPos:int;
private var yPos:int;
private var shapeColor:uint;
private var size:uint;
public function Star(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000,size:uint=20)
{ ...
A common naming convention used in ActionScript is to start the names of private variables with an underscore. You will rename the variables and use getters and setters in the next section. You are using names without the underscore right now so that you don't have to rename all the variables inside the drawStar() function.
public function Star(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000, size:uint=20){
this.xPos=xPos;
this.yPos=yPos;
this.shapeColor=shapeColor;
this.size=size;
super();
drawStar();
}
init() function, comment out the line of code calling the drawStar() function.bigStar of type Star and pass to the constructor the same values you passed to the drawStar() method before.var bigStar:Star=new Star(10,10,0x0000FF,40);
private function init():void{
addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
//drawStar(10,10,0x0000FF,40);
var bigStar:Star=new Star(10,10,0x0000FF,40);
this.addElement(bigStar);
}
else if(s=="star"){
//drawStar(mouseX,mouseY,shapeColor);
var star:Star=new Star(mouseX,mouseY,shapeColor);
else if(s=="star"){
//drawStar(mouseX,mouseY,shapeColor);
var star:Star=new Star(mouseX,mouseY,shapeColor);
shapeCanvas.addElement(star);
}
Refactoring by abstracting code involves redesigning your classes and their inheritance and implementation. Examples include creating getters and setters; creating subclasses, superclasses, and interfaces; using polymorphism; consolidating conditional expressions; and much more. Just as for extracting refactoring, the purpose of abstracting code is to minimize duplication of code and maximize code reuse.
The Star class has properties to set the location, color, and size of the star that is drawn. Whenever the value of one of these properties is set, the star needs to be redrawn. To provide this behavior, create getters and setters for the properties.
xPos in its declaration statement to select it.

xPos setter, call the drawStar() method. If the value of xPos changes, the star needs to be redrawn in the new position.public function set xPos(value:int):void
{
_xPos = value;
drawStar();
}
yPos, shapeColor, and size. Your code should appear as shown here.package
{
import mx.core.UIComponent;
public class Star extends UIComponent
{
private var _xPos:int;
private var _yPos:int;
private var _shapeColor:uint;
private var _size:uint;
public function Star(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000,size:uint=20)
{
this.xPos=xPos;
this.yPos=yPos;
this.shapeColor=shapeColor;
this.size=size;
super();
drawStar();
}
public function get size():uint
{
return _size;
}
public function set size(value:uint):void
{
_size = value;
drawStar();
}
public function get shapeColor():uint
{
return _shapeColor;
}
public function set shapeColor(value:uint):void
{
_shapeColor = value;
drawStar();
}
public function get yPos():int
{
return _yPos;
}
public function set yPos(value:int):void
{
_yPos = value;
drawStar();
}
public function get xPos():int
{
return _xPos;
}
public function set xPos(value:int):void
{
_xPos = value;
drawStar();
}
private function drawStar():void{
graphics.lineStyle(6, shapeColor, 1, false, "normal", "square", "miter", 8);
graphics.moveTo(xPos+size/2,yPos);
graphics.lineTo(xPos+size-size/6,yPos+size);
graphics.lineTo(xPos,yPos+size/3);
graphics.lineTo(xPos+size,yPos+size/3);
graphics.lineTo(xPos+size/6,yPos+size);
graphics.lineTo(xPos+size/2,yPos);
}
}
}
public function Star(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000, size:uint=20)
{
this._xPos=xPos;
this._yPos=yPos;
this._shapeColor=shapeColor;
this._size=size;
super();
drawStar();
}
onClick for the click event. Be sure to choose MouseEvent from the code hinting pop-up menu so the import is written for you.import flash.events.MouseEvent;
public function Star(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000, size:uint=20)
{
this.xPos=xPos;
this.yPos=yPos;
this.shapeColor=shapeColor;
this.size=size;
super();
drawStar();
addEventListener(MouseEvent.CLICK,onClick);
}
private function onClick(e:MouseEvent):void{
size+=10;
}

private function drawStar():void{
graphics.clear();
graphics.lineStyle(6, shapeColor, 1, false, "normal", "square", "miter", 8);
...

So far youhave created a class to create stars, but none of the other shapes. Ifyou continued to refactor and move the code creating the other shapesinto their own class files, you would quickly find that each of theclasses have similarities, specifically each would have the sameproperties (xPos, yPos, shapeColor, and size)and each would have a method to draw its graphics. When you haveclasses with the same properties and/or methods, you can minimizeduplication of code and simplify maintenance by defining a superclassand making your classes extend this class. This way, you only have todefine that shared code once and maintain it in one place instead of inmultiple places. To implement this, you are going to define asuperclass called AShape that all of the shape classes should extend.Because most of the code in the existing Star class will be go in theAShape class, you are just going to rename the Star class to AShape,make a few changes, and then create a new subclass called Star.
Note: You are using the name AShape instead of Shape, because the flash.display package already has a class called Shape.
bigStar and star have been changed to type AShape. Next, you are going to create the Star subclass and you will change these back to type Star.
var bigStar:Star=new Star(10,10,0x0000FF,40);
var star:Star=new Star(mouseX,mouseY,shapeColor);
private to protected so they are accessible to any subclasses.protected var _xPos:int;
protected var _yPos:int;
protected var _shapeColor:uint;
protected var _size:uint;
protected function drawStar():void{
drawStar() function.package
{
public class Star extends AShape
{
public function Star(xPos:int=0, yPos:int=0, shapeColor:uint=0x000000, size:uint=20)
{
super(xPos, yPos, shapeColor, size);
}
protected function drawStar():void{
graphics.clear();
...
}
}
}
override in front of the function definition.override protected function drawStar():void{
drawStar() function, and save the file.Next, create Circle and Square classes that extend the AShape class.
drawStar(). You will change this to a better name in the following section on Rename refactoring.else if(s="circle") block of the onMouseMove() function.circle of type Circle and pass to the constructor the mouse position and the selected color.if(s=="circle"){
var circle:Circle=new Circle(mouseX,mouseY,shapeColor);
}
if(s=="circle"){
var circle:Circle=new Circle(mouseX,mouseY,shapeColor);
shapeCanvas.addElement(circle);
}
if(s=="square"){
var square:Square=new Square(mouseX,mouseY,shapeColor);
}
if(s=="square"){
var square:Square=new Square(mouseX,mouseY,shapeColor);
shapeCanvas.addElement(square);
}
onMouseDown() method, delete the two lines of code invoking the lineStyle() and fillColor() methods. The colors and styles are not set and controlled within the shape classes.If you take a look at the code inside the onMouseMove()function, you will see that you execute almost the same code insideeach of the parts of the conditional logic block: you create a newshape and then you add it to the display list. All that is different isthe type of shape created. In this next section, you replace thissimilar, repeated code to instead use polymorphism, where an instanceof a subclass can be used wherever an instance of the superclass isexpected.
onMouseMove(), declare a variable called shape of type AShape before the conditional logic.private function onMouseMove(e:MouseEvent):void{
var shape:AShape;
if(s=="circle"){ ...
shape to the shapeCanvas.private function onMouseMove(e:MouseEvent):void{
var shape:AShape;
if(s=="circle"){
var circle:Circle=new Circle(mouseX,mouseY,shapeColor);
shapeCanvas.addElement(circle);
}
else if(s=="square"){
var square:Square=new Square(mouseX,mouseY,shapeColor);
shapeCanvas.addElement(square);
}
else if(s=="star"){
//drawStar(mouseX,mouseY,shapeColor);
var star:Star=new Star(mouseX,mouseY,shapeColor);
shapeCanvas.addElement(star);
}
shapeCanvas.addElement(shape);
}
else if(s=="circle") block, delete the declaration of the circle variable and instead set shape equal to the instance of the Circle class and delete the addElement() call.if(s=="circle"){
shape=new Circle(mouseX,mouseY,shapeColor);
}
if(s=="square"){
shape=new Square(mouseX,mouseY,shapeColor);
}
if(s=="star"){
shape=new Star(mouseX,mouseY,shapeColor);
}
Refactoring by movinginvolves moving classes and packages (folders of classes) to newlocations. If you move a class or package of classes from one locationto another and the development environment (Flash Builder 4 in yourcase) did not support move refactoring, you would have to search forall references to that class or any classes in the package you weremoving and change the package declarations and any import statements orfully qualified references to the classes to reflect the new location.Fortunately, Flash Builder 4 does support move refactoring and will dothis updating of all dependent files for you. You can move refactor byeither using a Move menu selection or dragging and dropping classes andpackages in the Package Explorer.
Start by creating a new package and moving the shape classes to it.

Note: A best practice is to make all of your packages start with lower-case letters. Upper-case letter are usually only used to name classes (for example, Star) or start new words in the middle of names (like MyStar). The later is often referred to as camel-case.
Right now, it simply says package with nothing after it because the class is located in the default package. In Flex the package declaration must always match the folder structure from the default package where the main Application file is located (or any other classpath).

If the class was referenced anywhere as a literal string (i.e.inside quotation marks), you would also need to select the check box toUpdate textual occurrences in string literals. In this case, you get apreview dialog box that displays each reference to the class in yourcode and you need to approve individually each reference you want tochange. You have no textual references to the Star class in yourcurrent code, but this first time you move refactor, you are going topreview each change before it is made. To skip this, you could clickthe OK button instead of the Preview button.

import statement so the compiler can locate the Star class when it is not in the default package. Leave the checkbox next to this change in the list selected so this change is made.
Next,create another new package and move the shapes package to it. Why wouldyou want to do this? There are two main reasons. The first is that whenyou create more and more classes, you want to organize them in logicalfolders and subfolders where classes with similar or relatedfunctionality are grouped together. Secondly, you want to create andguarantee unique fully qualified names for all the classes. The fullyqualified name of a class is its class name prefaced by its package,for example shapes.Star. To avoid ever having two classes withidentical fully qualified class names, a common practice is to use apackage folder structure corresponding to reverse domain names, forexample com.adobe.samples.shapes.Star. Because domain names are unique,this usually guarantees uniqueness of class names when they are sharedand used in multiple applications and are used with other classes sothey can be differentiated, for example com.adobe.samples.Star vs.com.anothercompany.Star.



Note: Because there is only one folder in each of the containing folders, Flash Builder 4 shows all the folders together as a single package. If you created new folders inside of the com or adobe folders (by selecting New -> Folder as you will do next), the individual folders in the package would be displayed instead.
import statements have been changed to refer to the new package.import com.adobe.shapes.AShape;
import com.adobe.shapes.Square;
import com.adobe.shapes.Circle;
import com.adobe.shapes.Star;
package com.adobe.shapes

import com.adobe.samples.shapes.AShape;
import com.adobe.samples.shapes.Square;
import com.adobe.samples.shapes.Circle;
import com.adobe.samples.shapes.Star;
Refactoring by renaming involves renaming classes and class members (its properties, methods, and events which make up its API, its Application Programming Interface). Although not difficult to do, using descriptive, appropriate names is extremely important to make the APIs as easy to use as possible so the developer does not have to look at the source code or read documentation to figure out how the class and its members should be used. Flash Builder supports rename refactoring just as it does move refactoring, so when you rename classes and packages, it updates all the source code and dependent files appropriately. You can rename refactor by either using a Rename menu selection in the Package Explorer or in the class file itself.
Start by renaming your Star class to AStar.
Alternately, you can right-click on the Star class under the Star.as file and select Rename... and get the same result (see Figure 22).
import statement and the code in the init() and onMouseDown() functions creating Star instances have all been updated to reflect the new name.Now use a second way to rename the class.
Next, rename a method.
drawStar() method, and select Rename... (see Figure 25).
draw() matching the new name of the method.drawStar() method was also changed to draw(). When you change the name of a method in a superclass, the name of any overriden function declarations in subclasses are also changed.Next, use a second way to rename class members.
s variable to select it (see Figure 26). You can select it in the variable declaration or anywhere it is referenced.
Next, rename a property in an MXML class file.
Now use a second way to rename an object.
<mx2:ColorPicker> tag and double-click the value of the id property, colorPicker (see Figure 28). You can also select colorPicker anywhere else it is used the file.
Refactoring code is all about cleaning up your code, making it more legible, logical, and reusable. In this tutorial, you have refactored the drawing application by renaming, moving, extracting, and abstracting code. In this last section, you finish up with perhaps the simplest way to clean up your code, by formatting it. In previous versions of Flex Builder, you had to format manually line by line or you had to use a separate program or a separate Eclipse plugin like FlexFormatter. Although not as feature-complete as that plugin or some others, Flash Builder 4 does now have several formatting features built-in to help format your code. Specifically, you can specify defaults for generated and pasted code, you can run a code clean up that fixes all your indentations to be consistent, and you can specify your own template files to be used when you create new files. At this time, Flash Builder 4 does have the ability to clean up your braces, change attribute placement, or add or remove spaces after the code has been written.
Start by locating the default format settings.
Next, format the code in your ActionScript and MXML files using the settings in the Preferences dialog box.
In addition to formatting existing code, you can also specify the formatting for the creation of new files including ActionScript files, classes, and interfaces; CSS files; and MXML web applications, AIR applications, components, and modules. You can edit the existing templates being used or import new templates of your own to use.
Note: You can also export the existing templates, modify them, and then import the new files so you can have multiple template files to choose between.
Congratulations! You have now successfully refactored your application by extracting, abstracting, moving, renaming, and formatting your code.
In this tutorial, you learned to refactor Flex 4 applications with Flash Builder 4. You started with a Flex drawing application that had all of its code in a single file and ended with code separated into multiple class files. You extracting and abstracted code into separate functions, classes, subclasses, and superclasses to make the code more reusable and extendable; you moved and renamed files, packages, and class members to make the code more understandable; and you formatted the code to make the code more readable.
Remember that refactoring is the process of making small, incremental changes to your code to make it more logical, readable, understandable, and extendable. Although you are not adding or changing any functionality while refactoring, whenever you change your code you run the risk of breaking it. You should continually test your code as you refactor. In this tutorial, you tested the application after each change to make sure it still worked. A more robust method of testing application functionality is to use unit testing. To learn more about using unit testing, see the Creating applications for testing in the Flex 4 documentation.
To learn more about creating and customizing Flex 4 applications, see the Flex 4 documentation, the Flex 4 in a Week video series and exercises, and the Adobe Flex Developer Center.
To learn more about refactoring applications, visit the refactoring page on wikipedia and visit Martin Fowler's refactoring web site and/or read his book, Refactoring: Improving the Design of Existing Code.

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License