Requirements
     
Prerequisite knowledge
Required products    

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.

Flash Builder (Download trial)    
User level      
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.
 

 
Import the starting Flex project

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.
 
  1. If you have not done so already, download and install Flash Builder 4.
  2. If you have not done so already, download and unzip the starting project for the tutorial.
  3. Open Flash Builder 4.
  4. Select File > Import Flex Project (FXP)...
  5. In the Import Flex Project dialog box, click the Browse button next to File (see Figure 1 below).
  6. In the Open dialog box, browse to where you saved the Flex4RefactoringTutorial_StartProject.fxp, select the file, and click the Open button.
  7. Back in the Import Flex Project dialog box, click the Finish button (see Figure 1).
file
Figure 1. Import a Flex project.

 

8. In the Package Explorer, click on the arrows to drill-down to Flex4RefactoringTutorial_StartProject -> src -> (default package) -> Drawing.mxml (see Figure 2).

file
Figure 2. Locate the main Application file.

 

9. Double-click Drawing.mxml to open the file.

10. To compile and test the application, click the green Run button on the main toolbar or select Run -> Run Drawing. A browser window should open and you should see your application.

11. Click and drag your mouse to draw.

12. Use the controls in the upper-right corner to change the shape and color and draw some more (see Figure 3).

file
Figure 3. Use the drawing application.

 

13. Return to Drawing.mxml in Flash Builder 4 and examine the code.

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.
 

 
Extract code into functions and classes

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.
 
 
Move code from one function to another
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.
 
  1. In the Script block, create a new private function called drawStar with no arguments and a return type of void.
private function drawStar():void{ }

2. Select the code in the else if(s=="star") block of the onMouseMove() function, cut it (Ctrl-X, Cmd-X, or Edit -> Cut), and then paste it inside your new drawStar() function.

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); }

3. Inside the else if(s=="star") block of the onMouseMove() function, call the drawStar() function.

else if(s=="star"){ drawStar(); }

4. Run the application. You should be able to select the star shape and draw exactly as before.

5. Return to Flash Builder and examine the 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.
 

6. Create two optional arguments for the function called xPos and yPos of type int and give them default values of 0.

private function drawStar(xPos:int=0,yPos:int=0):void{

7. Inside the drawStar() function, delete the two lines of code declaring and assigning the xPos and yPos variables. Your function should now appear as shown here.

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); }

8. Inside the else if(s=="star") block of the onMouseMove() function, pass xPos and yPos values equal to mouseX and mouseY to the drawStar() function.

else if(s=="star"){ drawStar(mouseX,mouseY); }

9. Run the application. It should work exactly as before. Next, you will create another star elsewhere, reusing the drawStar() function.

10. Inside the 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); }

11. Run the application. Even though you may expect to, you will not see a star in the upper left corner of the application yet.

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.
 

12. Copy the 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); ...

13. Run the application. You should now see a star in the upper left corner of the application (see Figure 4) and you can draw shapes exactly as before.

file
Figure 4. View the new star in the drawing application.
 
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.
 

14. Add a third optional argument to the drawStar() function called shapeColor of type uint and give it a default value of black (0x000000).

private function drawStar(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000): void{

15. Add a fourth optional argument to the drawStar() function called size of type uint and give it a default value of 20 pixels.

private function drawStar(xPos:int=0,yPos:int=0,shapeColor:uint=0x000000, size:uint=20):void{

16. Inside the drawStar() function, delete the var statement declaring and setting the size variable.

17. Inside the init() function, add additional parameters to the drawStar() function call to make a blue star that is 40 pixels high.

drawStar(10,10,0x0000FF,40);
18. 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); }

19. Run the application and then draw some new shapes. You should see a larger, blue star in the upper left corner of the application and you should be able to draw colored shapes as before.

 
Move a function into a new class
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.
 
  1. Select File -> New -> ActionScript Class.
  2. In the New ActionScript class dialog box, set the Name of the class to Star.
    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.
     
  3. Click the Browse button next to Superclass (see Figure 5).
file
Figure 5. Create a new ActionScript class.

 

4. In the Superclass dialog box, select UIComponent - mx.core and click OK (see Figure 6).

file
Figure 6. Specify a superclass.
 
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.
 
  1. In the New ActionScript Class dialog box, click the Finish button. You should see a new file called Star.as created with the following code.
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.
 

6. Return to Drawing.mxml and select the drawStar() function.

7. Cut the method (by selecting Edit -> Cut or pressing Cmd-X or Ctrl-X) and paste it inside the Star.as class file after the constructor (see Figure 7).

file
Figure 7. Move the function to the class file.

 

8. Inside the drawStar() method, change all the code to reference graphics instead of shapeCanvas.graphics.

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); }

9. Select the four arguments of the drawStar() method and move them so they are arguments for the constructor instead.

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); ...

10. Inside the constructor, call the new drawStar() method.

public function Star(xPos:int=0,yPos:int=0, shapeColor:uint=0x000000, size:uint=20) { super(); drawStar(); }

11. Before the constructor inside the Star class definition, declare four private variables called xPos, yPos, shapeColor, and size.

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.
 

12. Inside the constructor, assign the values of the variables passed to the constructor to the instance properties you just defined. You need to preface the instance variables with this. so they can be differentiated from the function arguments which have the same names.

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(); }

13. Save Star.as.

14. Return to Drawing.mxml and inside the init() function, comment out the line of code calling the drawStar() function.

15. Replace it with code creating an instance of the new Star class called 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);

16. Use the addElement() method of the Application class to add the star to the display list.

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); }

17. Inside the else if(s="star") block of the onMouseMove() function, comment out the line of code calling the drawStar() function, create an instance of the new Star class called star of type Star and pass to the constructor the same values you passed to the drawStar() method before.

else if(s=="star"){ //drawStar(mouseX,mouseY,shapeColor); var star:Star=new Star(mouseX,mouseY,shapeColor);

18. Add the star to the shapeCanvas by using the addElement() method. Add it to the shapeCanvas instead of the Application so the shapes will not appear on top of the controls in the upper-right corner.

else if(s=="star"){ //drawStar(mouseX,mouseY,shapeColor); var star:Star=new Star(mouseX,mouseY,shapeColor); shapeCanvas.addElement(star); }

19. Run the application and draw some stars. The application should work exactly as before.

 
Abstract classes and members

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.
 
 
Encapsulate properties using getters and setters
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.
 
  1. In Star.as, double-click the variable xPos in its declaration statement to select it.
  2. Right-click and select Source -> Generate Getter/Setter... (see Figure 8).
file
Figure 8. Select the menu option to create getters and setters.
 

3. In the Generate Getter/Setter dialog box, review all the default selections and then click OK (see Figure 9).

file
Figure 9. Generate getters and setters.

 

4. Examine the generated code.

5. Inside the 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(); }

6. Repeat steps 1-5 for the other three variables: 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); } } }

7. Inside the constructor, change the four assignment statements to set the corresponding private variables instead of calling the setter functions. Remember, the drawStar() method is called inside each of the setter functions. You only need to have it called once in the constructor.

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(); }

8. Save the file and run the application. It should work as before.You have not used the setters yet to change any of the properties atruntime.

9. Return to Star.as and inside the constructor, register to listen an event handler called 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); }

10. After the drawStar() function, create a function called onClick with one argument called e of type MouseEvent that increases the size variable by 10. Be sure to select MouseEvent from code-hinting so the import statement is written for you.

private function onClick(e:MouseEvent):void{ size+=10; }

11. Save the file and run the application. It should work as before.

12. Click several times on the big star in the upper-left corner. You should see larger stars created but they show up on top of the existing star instead of replacing it (see Figure 10). You need to clear the graphics before drawing the new star.

file
Figure 10. Click a star to make it bigger without clearing the existing graphics.

 

13. Return to Star.as and inside the drawStar() function, call the clear() method of the graphics object.

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

14. Save the file, run the application, and click several times on the big star again. As you click, the star should continue to get larger, but it should replace the smaller existing star (see Figure 11).

file
Figure 11. Click a star to make it bigger after clearing the existing graphics. 
 
Create a superclass
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.
 
  1. In the Package Explorer, right-click Star.as and select Rename...
  2. In the Rename Class dialog box, change the name to AShape and click OK.
    Note: You are using the name AShape instead of Shape, because the flash.display package already has a class called Shape.
     
  3. Return to AShape.as and look at your code. The name of the class and the name of the constructor function have changed.
  4. Return to Drawing.mxml and look at your code. The variables 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.
  5. Select File -> New -> ActionScript class.
  6. Givethe class a Name of Star, specify a Superclass of AShape, make sure theGenerate constructor from superclass checkbox is selected and thenclick Finish (see Figure 12).
file
Figure 12. Create the Star subclass.

 

7. Return to Drawing.mxml and inside the init() function, change bigStar back to be an instance of the Star class instead of AShape.

var bigStar:Star=new Star(10,10,0x0000FF,40);

8. Inside the onMouseMove() function, change star back to be an instance of the Star class instead of AShape.

var star:Star=new Star(mouseX,mouseY,shapeColor);

9. Run the application. It should work exactly as before. Star is now a subclass or AShape, but it is currently identical to it with no code of its own. Next, you need to move the code to draw the star from the superclass to the subclass.

10. Return to AShape.as and change the variables from 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;

11. Change the drawStar() function from private to protected so it is accessible to its subclasses and can be overridden (each subclass can have its own version of the function so they can draw different shapes).

protected function drawStar():void{

12. Save the file.

13. Copy the drawStar() function.

14. Return to Star.as and paste the function after the constructor.

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(); ... } } }

15. Save the file. You should get an error that the function is not marked for override.

16. Inside Star.as, add the keyword override in front of the function definition.

override protected function drawStar():void{

17. Save the file.

18. Return to AShape.as, delete the code inside the drawStar() function, and save the file.

19. Run the application and draw some stars. The application should work as before.

 
Create subclasses
Next, create Circle and Square classes that extend the AShape class.
 
  1. Openthe prewritten Circle.as and Square.as class files and examine thecode. These two classes have been written for you and are very similarto Star.as except they have different code to draw the shape. Rightnow, they all have a function called drawStar(). You will change this to a better name in the following section on Rename refactoring.
  2. Return to Drawing.mxml and delete (or comment out) the line of code drawing the circle inside the else if(s="circle") block of the onMouseMove() function.
  3. Replace it with code to create an instance of the Circle class called 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); }

4. Add the circle to the shapeCanvas by using the addElement() method.

if(s=="circle"){ var circle:Circle=new Circle(mouseX,mouseY,shapeColor); shapeCanvas.addElement(circle); }

5. Inside the else if(s="square") block of the onMouseMove() function, delete (or comment out) the line of code drawing the square.

6. Replace it with code to create an instance of the Square class called square of type Square and pass to the constructor the mouse position and the selected color.

if(s=="square"){ var square:Square=new Square(mouseX,mouseY,shapeColor); }

7. Add the square to the shapeCanvas by using the addElement() method.

if(s=="square"){ var square:Square=new Square(mouseX,mouseY,shapeColor); shapeCanvas.addElement(square); }
 
8. Inside the 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.

9. Runthe application and draw with each of the three shapes. The applicationshould work the same as before but now all the code to create theshapes is contained in separate class files.

 
Replace conditional logic with polymorphism
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.
 
  1. Inside 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"){ ...

2. After the conditional logic, add the 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); }

3. Inside the 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); }

4. Inside the else if(s=="square") block, delete the declaration of the square variable and instead set shape equal to the instance of the Square class and delete the addElement() call.

if(s=="square"){ shape=new Square(mouseX,mouseY,shapeColor); }

5. Inside the else if(s=="star") block, delete the declaration of the star variable and instead set shape equal to the instance of the Star class and delete the addElement() call.

if(s=="star"){ shape=new Star(mouseX,mouseY,shapeColor); }

6. Run the application and draw with each of the three shapes. The application should work exactly as before.

 
Move classes and packages

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.
 
 
Move a class to a different package
Start by creating a new package and moving the shape classes to it.
 
  1. Inthe Package Explorer, right-click on (default package) and select New-> Package. You can also select New -> Folder and fill out itsslightly different dialog box.
  2. In the New Package dialog box, enter a Name of shapes, and click the Finish button (see Figure 13). 
file
Figure 13. Create a new package.
 
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.
 
  1. Return to Star.as and look at the package declaration.
    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).
     
  2. In the Package Explorer, right-click on Star.as and select Move...
  3. In the Move dialog box, select the shapes package as the destination, make sure Update references is selected, and click Preview (see Figure 14).
file
Figure 14. Specify where to move a class.
 
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.
 
  1. Look at the list of changes to be performed at the top of the Move dialog box (see Figure 15).
file
Figure 15. Preview the list of changes that will be made.
 
  1. Click the Drawing.mxml change in the list. You should see the original code on the left and the new code on the right. In this case, the change is to add an 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.
  2. Click the Star.as change in the list and look at the new code on the right. The package declaration is changed to match the folder structure.
  3. Click the OK button to make the selected changes.
  4. Return to Star.as and look at the package declaration. It now says package shapes.
  5. Return to Drawing.mxml. You should now see an import statement at the top of the Script block.
  6. In the Package Explorer, right-click on Circle.as and select Move...
  7. In the Move dialog box, select the shapes package as the destination and click OK.
  8. In the Package Explorer, select Square.as and drag and drop it into the shapes folder. When you drop it, the Move dialog box should appear.
  9. In the Move dialog box, select the shapes package as the destination and click OK.
  10. In the Package Explorer, select AShape.as and drag and drop it into the shapes folder.
  11. In the Move dialog box, select the shapes package as the destination and click OK.
  12. In the Package Explorer, expand the shapes folder. It should contain your four classes (see Figure 16).
file
Figure 16. Locate the moved classes.
 
  1. Run the application and draw some shapes. The application should work exactly as before.
 
 
 
Move a package to a different package
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.
 
  1. In the Package Explorer, right-click on (default package) and select New -> Package.
  2. In the New Package dialog box, enter a Name of com.adobe, and click the Finish button (see Figure 17).
file
Figure 17. Create a new package.
 
  1. In the Package Explorer, right-click on the shapes package and select Move...
  2. In the Move Resources dialog box, use the arrows to drill-down and select src.com.adobe as the destination (see Figure 18).
file
Figure 18. Select the move destination.
 
  1. Click the OK button. You should now see a package called com.adobe.shapes in the Package Explorer (see Figure 19).
file
Figure 19. View the new package.
 
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.
 
  1. Return to Drawing.mxml and see that all of the 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;
  1. Return to AShape.as and see the package statement has been updated.
package com.adobe.shapes
  1. Similarly, examine the packages for the Star.as, Circle.as and Square.as files.
  2. In the Package Explorer, right-click on the com.adobe.shapes package and select New -> Folder.
  3. In the New Folder dialog box, select src.com.adobe as the parent (not src.com.adobe.shapes!), set the Folder name to samples, and click Finish (see Figure 20).
file
Figure 20. Create a new package subfolder.
 
  1. In the Package Explorer, click and drag the shapes folder into the samples folder.
  2. Return to Star.as and look at the package declaration. It now says package com.adobe.samples.shapes.
  3. Return to Drawing.mxml and see that all of the import statements have been changed to refer to the new package.
import com.adobe.samples.shapes.AShape; import com.adobe.samples.shapes.Square; import com.adobe.samples.shapes.Circle; import com.adobe.samples.shapes.Star;

 
Rename classes and members

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.
 
 
Rename a class in the Package Explorer
Start by renaming your Star class to AStar.
 
  1. In the Package Explorer, right-click on Star.as in the com.adobe.samples.shapes package and select Rename... (see Figure 21).
file
Figure 21. Rename a class using menu options for the class file in the Package Explorer.
 
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).
 
file
Figure 22. Rename a class using menu options for a class definition in the Package Explorer.
 
  1. In the Rename Class dialog box, set the New name to AStar, make sure Update references is selected, and click OK (see Figure 23).
file
Figure 23. Rename a class.
 
  1. Return to the newly renamed AStar.as and look at the class declaration and the name of the constructor. They now all say AStar, matching the name of the file. In Flex, the name of the class and the name of the ActionScript file it is located in must match.
  2. Return to Drawing.as. The 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.
 
  1. In AStar.as, double-click the name of the class to select it. You can select it in the class declaration or the constructor definition.
  2. Right-click and select Refactor -> Rename...(see Figure 24).
file
Figure 24. Rename a class using menu options for the class statement in the ActionScript file.
 
  1. In the Rename Class dialog box, set the New name to Star, make sure Update references is selected, and click OK.
  2. Locate the updated file name and references in Star.as and Drawing.mxml.

 

Next, rename a method.
 
  1. In the Package Explorer, drill-down to the class members of the AShape class, right-click the drawStar() method, and select Rename... (see Figure 25).
file
Figure 25. Rename a class member using menu options in the Package Explorer.
 
  1. In the Rename Class dialog box, set the New name to draw, make sure Update references is selected, and click OK.
  2. Return to AShape.as and look at the method declaration and the invocation of the method in the constructor and setter functions. They all now say draw() matching the new name of the method.
  3. Return to Star.as and see that the name of the 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.
 
  1. Return to Drawing.mxml, double-click the name of the s variable to select it (see Figure 26). You can select it in the variable declaration or anywhere it is referenced.
  2. Right-click and select Refactor -> Rename... (see Figure 26).
Rename a class member using menu options in an ActionScript or MXML file.
Figure 26. Rename a class member using menu options in an ActionScript or MXML file.
 
  1. In the Rename Class dialog box, set the New name to selectedShape, make sure Update references is selected, and click OK.
  2. Locate the updated references.

 

Next, rename a property in an MXML class file.
 
  1. Return to Drawing.mxml.
  2. In the Package Explorer, drill-down to the class members of the Drawing class, right-click the shapePicker DropDownList property, and select Rename... (see Figure 27).
Rename a class member defined with MXML using menu options in the Package Explorer.
Figure 27. Rename a class member defined with MXML using menu options in the Package Explorer.
 
  1. In the Rename Class dialog box, set the New name to shapeSelector, make sure Update references is selected, and click OK.
  2. Locate the updated references.

 

Rename a class member in MXML

Now use a second way to rename an object.
 
  1. In Drawing.mxml, locate the <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.
  2. Right-click^ and select Refactor -> Rename... (see Figure 28).
Rename a class member defined with MXML using menu options in the MXML file.
Figure 28. Rename a class member defined with MXML using menu options in the MXML file.
 
  1. In the Rename Class dialog box, set the New name to colorSelector, make sure Update references is selected, and click OK.
  2. Locate the updated references.

 
Format your code

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.
 
 
Examine the default formatting settings
Start by locating the default format settings.
 
  1. Select Window -> Preferences...
  2. In the Preferences dialog box, use the tree on the left-side to drill-down to Flash Builder -> Editors (see Figure 29). Look at the the default settings on the right that are used for formatting when code is generated or pasted.
Look at default settings for formatting code.
Figure 29. Look at default settings for formatting code.
 
  1. Drill-down further to Flash Builder -> Editors -> ActionScript Code and then Editors -> MXML Code and look at these settings.
  2. Navigate to Flash Builder -> Indentation and make sure you can see your existing code in Flash Builder beneath or to the side of the Preferences dialog box.
  3. Change the Tab size from 4 to 10 and then click the Apply button. You should see the larger indentations in your code.
  4. Change the value back to 4 and click Apply again.
  5. Navigate to Flash Builder -> Indentation -> ActionScript and then Indentation -> MXML and look at these settings. The indentation properties set here will be applied when you manually run a command to apply them to selected code in the next section.
  6. In Flash Builder -> Indentation -> ActionScript, uncheck the Indent package contents checkbox, look at the changes to the code example displayed in the dialog box, and then click OK (see Figure 30). The Preferences dialog box closes.
Change the indentation in generated class files.
Figure 30. Change the indentation in generated class files.
 
Format your existing code
Next, format the code in your ActionScript and MXML files using the settings in the Preferences dialog box.
 
  1. Return to Star.as. Your code should look exactly as before.
  2. Select all the code by selecting Edit -> Select All or pressing Ctrl-A or Cmd-A.
  3. With all the code selected, select Source -> Correct Indentation or press Ctrl-I or Cmd-I. The formatting of your class should now change based on the settings you set in the Preferences dialog box; specifically the package contents should no longer be indented and all other lines should be lined up with proper indentations that were not (see Figure 31).
Look at the new format of the class file.
Figure 31. Look at the new format of the class file.
 
  1. Move the constructor arguments to separate lines and change their spacing so they are not aligned (see Figure 32).
Alter the formatting of function arguments.
Figure 32. Alter the formatting of function arguments.
 
  1. Select all the code in the file again and select Source -> Correct Indentation or press Ctrl-I or Cmd-I. The arguments should now be aligned (see Figure 33).
Correct the formatting of function arguments.
Figure 33. Correct the formatting of function arguments.
 
  1. Return the indentation of the package to its previous format by changing the format setting back (select Window -> Preferences, navigate to Flash Builder -> Indentation -> ActionScript, check the Indent package contents checkbox, and click OK), selecting all the code (select Edit -> Select All or press Ctrl-A or Cmd-A) and then formatting the code again (select Source -> Correct Indentation or press Ctrl-I or Cmd-I).
  2. Return to Drawing.mxml.
  3. Change the indentation of the attributes in the Application tag and the indentation of other random lines of code.
  4. Select all the code in this file and select Source -> Correct Indentation or press Ctrl-I or Cmd-I. All the code and the attributes should now be aligned.
 
Change the format for new files
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.
 
  1. Select Window -> Preferences...
  2. In the Preferences dialog box, use the tree on the left-side to drill-down to Flash Builder -> File Templates.
  3. Under File Types, drill-down to ActionScript -> ActionScript Class and then look at the Template code (see Figure 34).
Locate the template used to generate ActionScript class files.
Figure 34. Locate the template used to generate ActionScript class files.
 
  1. Click the Edit button.
  2. In the Edit template dialog box, change the code formatting in some way (see Figure 35) and click the OK button.
Edit the template used to generate Actionscript class files.
Figure 35. Edit the template used to generate Actionscript class files.
 
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.
 
  1. In the Preferences dialog box, click the OK button.
  2. In the Package Explorer, right-click on the com.adobe.samples.shapes package and select New -> ActionScript Class.
  3. In the New ActionScript Class dialog box, enter a Name of Test and click Finish. You should see a new ActionScript class file with the formatting you specified when you modified the template. If you want, repeat steps 1-5 and return the template to its previous formatting.
Congratulations! You have now successfully refactored your application by extracting, abstracting, moving, renaming, and formatting your code.
 

 
Where to go from here

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.