Wednesday 28 July 2010

On Refactoring Methods

On Refactoring Methods
Report No.10
先端ソフトウェア工学Ⅰ Advanced Software Engineering I  


Ahmad EL GHAFARI
ID#1051133
Software Design Laboratory
NAIST, Japan July 2010


1. Extract Method
Suppose I have a software system that collects files found in media (e.g. hard disk). Starting from selected root directory by user, the system seeks folders, and sub folders, grabs the files, and then lists them for the user in UI platform.
User can specify whether all files should be listed all just files match particular category selected. User highlighted that only ZIP, PDF and Executable files only are of concern.

In case of all files found are listed. User required two features.
 User needs to sort (or group) the files matching a particular file type.
 User needs to see a list of files filtered by a selected type, i.e. ZIP type files.

Let’s show the initial class diagram of the system illustrated in Figure 1 File Collector System Initial Class Diagram



Figure 1 File Collector System Initial Class Diagram

Here, I want to focus only on the methods that we are interested in to highlight how refactoring using Extract Method eliminates duplicate code. We assume that the functionality of grabbing files from the selected root directory and every subfolder is defined as “collectAllFiles()” member method—in the class Collector and that it returns the result to data member “m_collectedFiles”. Here, I provide Source code of the system defined written in Java.

Collector.java
public class Collector {
private String m_rootDirectory;
private java.util.List m_collectedFiles =null;

public Collector(String rootDirectory){
this.m_rootDirectory = rootDirectory;
this.m_collectedFiles = collectAllFiles();
}

private java.util.List collectAllFiles(){
//go to the selected directory found in data member m_rootDirectory…

//collect the files, and check all folders and //subfolders under the root directry and //collect all //the files found

}
public String getCollectedFiles(){
String result=null;
java.util.Iterator files = this.m_collectedFiles.iterator();
while(files.hasNext()){
File file = (File) files.next();
result += file.fileInfo();
}
return result;
}
}

In the following discussions, we will mention the other two functions
“sortCollectedFilesByType()” and “filterCollectedFiles”.


Sorting Scenario

public String sortCollectedFilesByType(){
String result="\nCollected files sorted \n";
java.util.Iterator files = this.m_collectedFiles.iterator();
while(files.hasNext()){
File file = (File) files.next();
if (file.getType() == FileType.EXECUTABLE)
result += file.fileInfo();
}

files = this.m_collectedFiles.iterator();
while(files.hasNext()){
File file = (File) files.next();
if (file.getType() == FileType.PDF)
result += file.fileInfo();
}

files = this.m_collectedFiles.iterator();
while(files.hasNext()){
File file = (File) files.next();
if (file.getType() == FileType.ZIP)
result += file.fileInfo();
}
return result;
}
As we can see the same code chunk is repeated 3 times. We experience duplicate code.
Using Extract method that has the algorithm (logical chunk) in one method and call it in all places required.
public String sortCollectedFilesByType(){
String result="\nCollected files sorted \n";
result+=getFilesSortedAs(FileType.EXECUTABLE);
result+=getFilesSortedAs(FileType.PDF);
result+=getFilesSortedAs(FileType.ZIP);
return result; }

The Extracted Method is
private String getFilesSortedAs(
FileType selectedFileType){
String result="";

java.util.Iterator files = this.m_collectedFiles.iterator();

while(files.hasNext()){
File file = (File) files.next();
if (file.getType() == selectedFileType)
result += file.fileInfo();
}
return result;
}
As we can see the duplicate code was removed preserving the behavior of the system.


Figure 2 Collector Class Diagram after Extract Method Refactoring

In Figure 2 Collector Class Diagram after Extract Method Refactoring is shown how the Collector Class object refinement after applying Extract method refactoring.

2. Form Template Method
Filtering scenario
After the system listed all files, User chooses a type category from Filter menu. Then, the system should relist only the files matched the category. User interested in only three types of files, Executable, PDF and ZIP files.
One can define the method as the following

public String filterCollectedFiles(
FileType selectedFileType){
String result="\nCollected files filtered as " +selectedFileType+"\n";

java.util.Iterator files = this.m_collectedFiles.iterator();

while(files.hasNext()){
File file = (File) files.next();
if (file.getType() == selectedFileType)
result += file.fileInfo();
}
return result;
}
However, after some time, the developers collected new user requirements. User raised an issue of the need to filter collected files not only by Type category, but rather they need to be able to filter the files by file Title, and Size.

Let’s say do this. Copy the method two times changing the parameter for each to match the criteria. For filtering by title we provide a String selected title, and the same for size, as the following.

public String filterCollectedFiles(
String selectedTitle){
String result="\nCollected files filtered as " +selectedTitle+"\n";

java.util.Iterator files = this.m_collectedFiles.iterator();

while(files.hasNext()){
File file = (File) files.next();
if (file.getTitle().compareTo(selectedTitle)==0)
result += file.fileInfo();
}
return result;
}

public String filterCollectedFiles(
double selectedSize){
String result="\nCollected files filtered as " +selectedSize+"\n";

java.util.Iterator files = this.m_collectedFiles.iterator();

while(files.hasNext()){
File file = (File) files.next();
if (file.getSize()==selectedSize)
result += file.fileInfo();
}
return result;
}
It’s obvious how the logic is the same, and what differs is only the parameter sent via the methods signatures and the condition/checking criteria. This is only a duplicate in two places. But, suppose some new changes happen to the system requirements. For example, the system should filter files by “DateCreated”. In fact, requirements change is open and we need more abstract way of thinking to enhance extensibility of our system. Form Template Method can eliminate the duplicate code as the following.

Suppose we have an abstract Class Filter which is a superclass of all the alternative Filter classes in which each of the subclasses having the operation of filtering logic.
Refactoring by applying Form Template Method of the invariant logic of the filtering, will remove the duplicate code exist in every subclass, i.e. FilterType, FilterSize, etc. Each concrete sub class will tweak the algorithm for its needs with providing its own implementation variation, as following.
Filter class is abstract and have the Template method

final String filter()
And has abstract method that will be overridden by its children

abstract Boolean compare()
All the filtering types have the same algorithm—perform similar operations with a few minor differences—differ in the comparison. For example, one needs to compare two Strings of Title, second wants to compare Size of files of type “Double”, a third needs to compare the Type of the file, PDF, ZIP etc.
The Class Diagram and amendments listing made to the source code of the source code are given as following. Some refinements was made also two the Collector and File classes. A new data member of Type Filter was added to Collector.
We need to get the title and size of file, so two more get public methods were added, as shown in Figure 3 Template Method Pattern as Filter Class Hierarchy Class Diagram.


Figure 3 Template Method Pattern as Filter Class Hierarchy Class Diagram

Filter.java
public abstract class Filter {
abstract Boolean compare(
File file, Object selectedCriteria);

final String filterCollectedFiles(
java.util.List collectedFiles, Object selectedCriteria){

String result="\n Collected files filtered as " +selectedCriteria.toString()+"\n";

java.util.Iterator files =collectedFiles.iterator();

while(files.hasNext()){
File file = (File) files.next();
if (compare (file, selectedCriteria))
result += file.fileInfo();
}
return result;
}
}

Children of Filter Class are FilterType to filter by type, FilterTitle to filter by title and FilterSize to filter according to a selected size.
Here the source code of the Filter children.

FilterType.java
public class FilterType extends Filter{
@Override
Boolean compare(File file, Object selectedCriteria){
Return file.getType().toString().compareTo(
selectedCriteria.toString())==0 ? true:false;
}
}
FilterTitle.java
public class FilterTitle extends Filter {
@Override
Boolean compare(File file, Object selectedCriteria) {
return
(file.getTitle().compareTo(
selectedCriteria.toString())==0)?true:false;
}
}
FilterSize.java
public class FilterSize extends Filter{
@Override
Boolean compare(File file, Object selectedCriteria) {
return file.getSize()==Double.valueOf(selectedCriteria.toString()).doubleValue()?true:false;
}
}
What Happened to the three copies of our filtering method in Class Collector filterCollectedFiles ()?
Actually, the method and all its duplicates replaced by one just 3 lines, as following

public String filterCollectedFiles
(Object keyFilter, Filter filterType){

filter = filterType;
return filter.filter (m_collectedFiles, keyFilter);
}

We Formed Template Method “filter()” which is the invariant, while each inherited class will do its own check variance by introducing its own “compare()” From now on, any client will specify the filter type and the key filter passing them as parameters to our original method “filterCollectedFiles()” while this method will call the Template method “filter” in Filter Class. As a result, all code duplicates were eliminated.

3. Extract Class
Suppose now Customer raised a new requirement. They want the system not only print out the files information on screen, rather generating XML log files having information about listed files shown in screen. Also, they want the system read such XML log files in future. In addition, Customer wants also to generate log files for some files filtered in specific category (i.e. all PDF files).

Here, we can add a public method in Collector Class that generates log files having the list of collected files with their information. And, another method that reads generated log files. And a method generates a log file with filtered files according to some category. Here, we can see that with the first method and third method is the same if we passed the list of files as an argument to the methods parameters. And, as a result a duplicate code!

In addition, it turns out that two methods are not enough to do so, unless we are going to hard-coding or doing procedural programming. Since, generating an XML file needs creating a file system at storage media, and needs I/O functionalities, in addition to Serialization procedure and for reading we need Deserialization. Actually, this is not a job for Class Collector. In other words, we want to apply Object-Oriented and design patterns to exhibit clarity, modifiability and extensibility.

Let’s say that all the said amendments are taken and made to Class Collector. The result is Class Collector is doing too much work, which is a work that should be done by two classes. Extract Class refactoring witnesses the birth of a Class that is responsible of doing such kind of job. This class also needs as an attribute of type File as in Class Collector and another attribute of type Filter. Here, what we have is two features in two classes, common attributes, and common methods. In addition, The two classes Class Filter and Class File will be also aggregated to the new born Class which is unacceptable coupling in the system architecture that rises the complexity. The new Class called XMLGenerator.
As shown in Figure 4 The System Class Diagram with the .




Figure 4 The System Class Diagram with the Extracted Class XMLgenerator

Notice the common attributes “m_filter: Filter” and “m_collectedFiles : List” and the common method “filterCollectedFile()” in both classes. Also, the aggregation and composition among Collector and XMLGenerator and File and Filter classes are obvious which signs for high coupling and complexity of the system architecture.

According to Folwer’s refactoring catalogue [‎1], and as as desined in [‎3] “when you have two classes with similar features, create a superclass and move the common features to the superclass”, we can apply Extract Class refactoring to eliminate duplicate code in the two classes Class Collector and Class XMLGenerator. In fact, Extract Class will also solve the two coupling issues among the two said classes and the other two classes Filter and File. The Extracted Class called FileHandler acts as a superclass for Collector and XMLGenerator. The common attributes and method will be moved to the FileHandler As shown in Figure 5.


Figure 5 The System Class Diagram After Extracted Superclass FileHandler as Superclass for Collector and XMLGenerator

The code listing as the following

FileHandler.java
public abstract class FileHandler {
protected Filter filter =null;
protected java.util.List m_collectedFiles =null;

final String filterCollectedFiles(
Object keyFilter, Filter filterType){
filter = filterType;
return filter.filter(m_collectedFiles, keyFilter);
}
}
XMLGenerator.java
public class XMLGenerator extends FileHandler{

public XMLGenerator(java.util.List list)
{
super.m_collectedFiles=list;
}

public void generateXML(
Object keyFilter, Filter filterType)
{
if (keyFilter == null && filterType==null ) {
System.out.println("XML log file was generated successfully.\n");

//serialize files to xml
...
}else
{
String s=super.filterCollectedFiles (
keyFilter, filterType);

//serialize files to xml

...

System.out.println(
"files were filtered and XML log file was generated successfully." + "\n"+s+"\n")

}
}

The result is one code plays the role for many clients. The features that are called in many places as needed placed in one place with a newly extracted class called FileHandler removing the duplicate code, though the solution introduced might be not the ultimate, but at least showing how Extract Class refactoring can eliminate duplicate code.


4. Pull Up Method

In the previous section “Extract Class” we moved up the common method “filterCollectedFile()” that appeared in both Collector and XMLGenerator. We Pulled Up the method to the superclass FileHandler. Since the behavior was the same of the two methods, Pull Up Method refactoring worked by removing the duplicate code and now the algorithm (a piece of code) exists uniquely in one place in the system.

References




1. Matrting Folwer, Kent Beck, John Brant, William Opdyke, and Don Roberts. Refactoring: Improving the Design of Existing Code. Addison-Wesley, Reading, MA, 1999.
2. Robert France, Sudipto Ghosh, Eunjee Song, and Dae-Kyoo Kim. A metamodeling approach to pattern-based model refactoring. IEEE Software, pages 52-58, September 2003.
3. Sami Beydeda, Matthias Book, Volker Grunhn (Eds.). Model-Driven Software Development. Generic and Domain-Specific Model Refactoring Using a Model Transformation Engine, Jing Zhang, Yuehua Lin, and Jeff Gray, pages 199-206, Springer 2005.

Monday 12 July 2010

An Implementation of Bridge Pattern - Modellers Commit Capability



A cut of a report for a course of my master's programme at Nara Institute of Science and Technology, Japan.