Вы находитесь на странице: 1из 17

Software Design Laboratory

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

Ahmad EL GHAFARI
ID#1051133

1
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

Collector

-m_rootDirectory
-m_collectedFiles: List<File>

-collectAllFiles()
+getCollectedFiles()
+sortCollectedFilesByType()
+filterCollectedFiles()

0..*
File

-m_title
<<enumeration>>
-m_path
FileTypes
-m_fileType
-m_dateCreated +EXECUTABLE
-m_size +PDF
+ZIP
+fileInfo()
+getType()

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

2
“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<File> m_collectedFiles =null;

public Collector(String rootDirectory){


this.m_rootDirectory = rootDirectory;
this.m_collectedFiles = collectAllFiles();
}

private java.util.List<File> 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”.

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

4
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.

Collector

-m_rootDirectory
-m_collectedFiles: List<File>

-collectAllFiles()
+getCollectedFiles()
+sortCollectedFilesByType()
+filterCollectedFiles()
-getFilesSortedAs()

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.

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

6
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.

7
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 showin in Figure 3 Template Method Pattern as Filter Class
Hierarchy Class Diagram.

Collector
-m_rootDirectory
<<abstract>> -m_collectedFiles: List<File>
Filter 0..* -m_filter: Filter
-collectAllFiles()
<<final>>filter() +getCollectedFiles()
<<abstract>>compare() +sortCollectedFilesByType()
+filterCollectedFiles()
-getFilesSortedAs()

FilterType FilterSize
0..*
<<override>>compare() <<override>>compare() File
<<enumeration>>
-m_title
FileTypes
FilterTitle -m_path
-m_fileType +EXECUTABLE
-m_dateCreated +PDF
<<override>>compare() -m_size +ZIP
+fileInfo()
+getType()
+getTitle()
+getSize()

Figure 3 Template Method Pattern as Filter Class Hierarchy Class


Diagram

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

8
final String filterCollectedFiles(
java.util.List<File> 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;

9
}
}
FilterSize.java
public class FilterSize extends Filter{
@Override
Boolean compare(File file, Object selectedCriteria) {
return
file.getSize()==Double.valueOf(selectedCriteria.toS
tring()).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.

10
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 .

11
<<abstract>>
Filter

<<final>>filter()
<<abstract>>compare()

FilterType FilterSize

<<override>>compare() <<override>>compare()

FilterTitle

<<override>>compare()

XMLGenerator
File
-m_collectedFiles: List<File>
-m_title -m_filter: Filter
-m_path
-m_fileType +filterCollectedFiles()
-m_dateCreated +generateXML()
-m_size -serialize()
+readXML()
+fileInfo() -deserialize()
+getType()
+getTitle()
+getSize()
Collector

-m_rootDirectory
-m_collectedFiles: List<File>
-m_filter: Filter
<<enumeration>>
FileTypes -collectAllFiles()
+getCollectedFiles()
+EXECUTABLE
+sortCollectedFilesByType()
+PDF
-getFilesSortedAs()
+ZIP
+filterCollectedFiles()

Figure 4 The System Class Diagram with the Extracted Class


XMLgenerator
Notice the common attributes “m_filter: Filter” and “m_collectedFiles : List<File>”
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

12
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.

<<abstract>>
Filter 0..*

<<final>>filter()
<<abstract>>compare()

FilterType FilterSize

<<override>>compare() <<override>>compare()

FilterTitle

<<override>>compare()

File <<abstract>>
FileHandler
-m_title
-m_path #m_filter: Filter
-m_fileType #m_collectedFiles: List<File>
-m_dateCreated +filterCollectedFiles(Object keyFltr, Filter fltrType)
-m_size 0..*

+fileInfo()
+getType()
+getTitle()
+getSize()
Collector
XMLGenerator
-m_rootDirectory

<<enumeration>> -collectAllFiles() +generateXML()


FileTypes +getCollectedFiles() -serialize()
+sortCollectedFilesByType() +readXML()
+EXECUTABLE -getFilesSortedAs() -deserialize()
+PDF
+ZIP

Figure 5 The System Class Diagram After Extracted Superclass


FileHandler as Superclass for Collector and XMLGenerator

The code listing as the following

13
FileHandler.java
public abstract class FileHandler {
protected Filter filter =null;
protected java.util.List<File> 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<File> 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

...

14
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.

15
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.

16
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.

17

Вам также может понравиться