Chapter 6: Code Refactoring Omar Meqdadi SE 3860 Lecture 6 Department of Computer Science and...

Post on 31-Dec-2015

218 views 3 download

Transcript of Chapter 6: Code Refactoring Omar Meqdadi SE 3860 Lecture 6 Department of Computer Science and...

Chapter 6: Code Refactoring

Omar Meqdadi

SE 3860 Lecture 6

Department of Computer Science and Software Engineering University of Wisconsin-Platteville

2

Topic Covered

Refactoring Refactoring Categories Refactoring Limitations Refactoring Tools

3

Refactoring

Process of changing a software system in such a way that it Does not alter the external behavior of the code Improves its internal structure

A program restructuring operation to support Design evolution Reuse of object oriented frameworks Preserve the behavioral aspects of the program

Refactoring Specifies

Source to source transformation Remain inside the same language, e.g., C++ to C++ Does not change the programs behavior – according to a

test suite Originally designed for object-oriented languages, but

can also be applied to non-object oriented language features, (i.e., functions)

Refactoring: Why?

Design Preservation Design Evolution Problems

Code changes often lead to a loss of the original design Loss of design is cumulative:

Difficulties in design comprehension Difficulties in preserving design More rapid decay of design

Solution: Refactoring improves the design of existing code

Refactoring: Why?

Comprehension Developer's are most concerned with getting the program

to work, not about future developers Refactoring makes existing code more readable Increases comprehension of existing code, leading higher

levels of code comprehension Often applied in stages

Refactoring: Why?

Debugging Improved program comprehension leads to easier

debugging Increased readability leads to the discovery of possible

errors Understanding gained during debugging can be put back

into the code

Refactoring: Why?

Faster Programming Counterintuitive argument made by Fowler Good design is essential for rapid development Poor design allows for quick progress, but soon slows the

process down Spend time debugging Changes take longer as you understand the system and find

duplicate code

Refactoring: When?

Adding Functionality Comprehension of existing program Preparation for addition

Debugging Comprehension of existing program

Code Review Preparation for suggestions to other programmers Stimulates other ideas Remove bad code smells

Refactoring Catalog

Refactoring entry composed of: Name Summary Motivation Mechanics Examples

Refactoring: Categories

Composing Methods Creating methods out of inlined code

Moving Features Between Objects Changing of decisions regarding where to put

responsibilities Organizing Data

Make working with data easier

Refactoring: Categories

Simplifying Conditional Expressions Making Method Calls Simpler

Creating more straightforward interfaces Dealing with Generalization

Moving methods around within hierarchies Big Refactoring

Refactoring for larger purposes

Composing Methods

Extract Method Inline Method Inline Temp Replace Temp with Query Introduce Explaining Variables Split Temporary Variable Remove Assignments to Parameters Replace Method with Method Object Substitute Algorithm

Remove Assignments to Parameters

Example: Remove Assignments to Parameters

Extract Methods

Create a new method, and name it after the intention of the method (name it by what it does, not by how it does it)

Copy the extracted code from the source method into the new target method

Scan the extracted code for references to any variables that are local in scope to the source method

See whether any temporary variables are used only within this extracted code. If so, declare them in the target method as temporary variables

Extract Methods (cont.)

Look to see whether any local-scope variable are modified by the existing code

Pass into the target method as parameters local scope variables that are read from the extracted code

Compile when you have dealt with all the locally- scoped variables

Replace the extracted code in the source method with a call to the target method

Compile and test

Extract Methods

void compAndPrint() { // compute X // compute Y ... // print X // print Y ... // store X // store Y}

void compute () { // compute X // compute Y print(X, Y); // store X // store Y}

void print(X,Y) { // print X // print Y}

Inline Methods

void compute () { X = next(X); Y = next(Y);}

int next(N) { return N++;}

void compAndPrint() { ... X++; Y++; ...}

Inline Temp

Example:

double basePrice = anOrder.basePrice();return (basePrice > 1000) return (anOrder.basePrice > 1000)

Replace Temp with Query

When: using a temporary variable to hold the result of an expression.

double rectarea = length * width; if (rectarea > 1000) return rectarea * 0.95; else return rectarea * 0.98;

If ( RectArea( ) > 1000 ) return RectArea( ) * 0.95;else return RectArea( ) * 0.98;}

double RectArea( ){ return length * width;}

Split Temp Variable

When: using a temporary variable to hold several expressions.

double temp = 2 * (_height * _widgth);cout << “Perimeter is “ << temp << endl;temp = _height * _width; cout << “Area is “ << temp << endl;

double perimeter = 2 * (_height * _widgth);cout << “Perimeter is “ << perimeter << endl;double area = _height * _width; Cout << “Area is “ << area << endl;

Introduce Explaining Variable

When: You have a complicated expression. Then, Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.

// return = baseprice – quantitydiscount + shippingreturn (quantity*price)-((quantity-50)*discount) +price * 0.05

double baseprice = quantity * price;double quantitydiscount = (quantity-50)*discount;double shipping =price * 0.05;return baseprice – quantitydiscount + shipping;

Moving Object Features

Move Method Move Field Extract Class Inline Class Hide Delegate Remove Middle Man Introduce Foreign Method Introduce Local Extension

Move Method

When: if a method is used by another class more than the class on which its defined, move it to the other class

class Student { bool IsEnroll(Course course) { return (course.getStudents().contains(this)); } }

class Course { Student students[size]; Student * getStudents() { return students; } }

class Student { }

public class Course { Student students[size]; bool isEnroll(Student student) { return students.contains(student); } }

Extract Class

class Student{ private: string name; string street; string city; string state; }

class Student{ private: string name; Address address; }

class Address{ private: string street; string city; string state; }

Inline Class

When: a class that isn’t doing too much and shouldn’t be around any more. Then, move all its features to another class. So inline class is the reverse of Extract Class

Hide Delegate

When: a client calls a delegate class of an object Refactoring: create methods on the server to hide the delegate

Client must know about Person and Department

Person must know about department

Client only calls Person

Person calls department

Hide Delegate Class Person {

Department _department;

public Department getDepartment()

{ return _department;}

public void setDepartment (Department arg)

{ _department = arg;}

} // end person

Class Department {

private String _chargeCode;

private Person _manager;

public Department (Person manager)

{ _manager = manager; }

public Person getManager()

{ return _manager; }// end Dept

Client Class:

joe.getDepartment().getManager();

Person Class:

public Person getManager () {

return _department.getManager();

} // end getManager

Client Class:

manager = joe.getManager();

Remove Middle Man

When: after several “Hide delegate”, the server becomes just a middle man for all the delegate methods.

Refactoring: Change the client to call the delegate directly

Client only calls Person

Person calls department

Client must know about Person and Department

Person must know about department

Introduce Foreign Method

When: a server class needs an additional methods, but you can’t modify it

Refactoring: Create the method in the client class Set one of the argument of the method to the instance of

the server class StrDate newStart = new Date

(previousEnd.getYear(),

previousEnd.getMonth(),

previousEnd.getDate()+ 1);

Date newStart = nextDat(previousEnd);

private static Date nextDay (Date arg)

{

return new Date (arg.getYear(),arg.getMonth(),

arg.getDate() + 1);

} // end nextDay

Introduce Local Extension

When: a class needs several additional methods, but you can’t modify it ( such as server class)

Refactoring: Create a new class that contains these additional methods Make the new class a subclass of the original one

(inheritance) or use wrapper approach (delegation)

Organizing Data

Self Encapsulate Field Replace Data Value with Object Change Value to Reference Change Reference to Value Replace Array with Object Duplicate Observed Data Change Unidirectional Association to Bidirectional Change Bidirectional Association to Unidirectional

Organizing Data (cont.)

Replace Magic Number with Symbolic Constant Encapsulate Field Encapsulate Collection Replace Record with Data Class Replace Type Code with Class Replace Type Code with Subclasses Replace Type Code with State/Strategy Replace Subclass with Fields

Change Reference to Value

void swap ( int * x , int * y){ int temp = *x; *x = *y; *y = temp;}void main (){ int a = 5; int b = 7; swap (&a , &b);}

void swap ( int &x , int &y){ int temp = x; x = y; y = temp;}void main (){ int a = 5; int b = 7; swap (a , b);}

Replace Array with Object

String[] row = new String[3];row[0] = “Platteville”;row[1] = “15”;

Performance row = new Performance();row.setName(“Platteville”);row.setWins(“15”);

Encapsulate Field

Replace Magic Number with Constant

Example:

double Energy (double m, double h) {

return m * 9.81 * h;

}

static Final double GRAVITATIONAL_CONSTANT * 9.81;

double Energy (double m, double h) {

return m * GRAVITATIONAL_CONSTANT * h;

}

Simplifying Conditional

Decompose Conditional Consolidate Conditional Expression Consolidate Duplicate Conditional Fragments Remove Control Flag Replace Nested Conditional with Guard Clauses Replace Conditional with Polymorphism Introduce Null Object Introduce Assertion

Decompose Conditional

Example: Decompose Conditional

Consolidate Conditional

double disabilityAmount() { if (_seniority < 2) return 0; if (_monthsDisabled > 12) return 0; if (_isPartTime) return 0; // do smoething else}

double disabilityAmount() {{ if (isNotEligableForDisability()) return 0; // do something else}

bool isNotEligableForDisability(){ if (_seniority < 2 || _monthsDisabled > 12 || _isPartTime() ) return false; else return true;}

Consolidate Duplicate Conditional Fragments

if (isSpecial()) { total = price * 0.95; send(); }else{ total = price * 0.98; send();}

if (isSpecial()) { total = price * 0.95;}else{ total = price * 0.98;}

Send();

Introduce Null Objects

When: for null or default behavior, use inheritance

class Animal { Voice MakeSound() { return voice; } }

Animal obj = new Animal;if (obj == null) voice = Voice.basic();else voice = obj.MakeSound();

class Animal{ Voice MakeSound() { return voice; }}

class NullAnimal : public Animal{ Voice MakeSound() { return Voice.basic(); }}

Replace Nested Conditional with Guard

When: A method has unclear conditional behavior. Then , use Guard Clauses for all the special cases

double getAmount() { double result; if (isDead ()) result = deadAmount(); else { if (isSeparated( ) ) result = separatedAmount(); else { if (isRetired ( ) ) result = retiredAmount(); else result = normalPayAmount(); } } return result;}

double getAmount() { if (isDead ( ) ) return deadAmount(); if (isSeparated ( ) ) return separatedAmount(); if (isRetired ( ) ) return retiredAmount(); return normalPayAmount();}

Replace Conditional with Polymorphism

class Bird {……double getSpeed() { switch (_type) { case EUROPEAN: return getBaseX(); case AFRICAN: return getBaseX() – _number; }}…..

Introduce Assertion

When: the code assumes something. Then, make the assumption explicit with an assertion.

double getExpenseLimit() {

// should have either expense limit or // a primary // project

 return (_expenseLimit != NULL_EXPENSE) ?

        _expenseLimit:

       _primaryProject.getMemberExpenseLimit();

   }

 

double getExpenseLimit() {      

Assert.isTrue (_expenseLimit != NULL_EXPENSE || _primaryProject != null);      

return (_expenseLimit != NULL_EXPENSE) ? 

          _expenseLimit:          

_primaryProject.getMemberExpenseLimit(); 

  }  

Remove Control Flag

When: remove a control flag and use break or return void checkSecurity( ) {      

bool found = false;      

for (int i = 0; i <length; i++) {          

if (! found) {             

if(people[i].equals("Don")){               

sendAlert();               

found = true;   }             

if(people[i].equals ("John")){               

sendAlert();               

found = true;   }

           }     

  }  

}  

void checkSecurity( ) {         

for (int i = 0; i <length; i++) {          

  if(people[i].equals("Don")){               

sendAlert();               

break;   }             

if(people[i].equals ("John")){               

sendAlert();               

break;   }

  }  

}  

Simplifying Method Calls

Rename Method Add Parameter Remove Parameter Separate Query from Modifier Parameterize Method Replace Parameter with Explicit Methods Preserve Whole Object Replace Parameter with Method

Simplifying Method Calls(cont.)

Introduce Parameter Object Remove Setting Method Hide Method Replace Constructor with Factory Method Encapsulate Downcast Replace Error Code with Exception Replace Exception with Test

Remove Setting Method

When: A field should be set at creation time and never altered. Then, remove any setting method for that field.

class Account {   

private String _id;   

Account (String id)

{  setId(id);   }   

void setId (String arg)

{  _id = arg;   }

class Account {   

private String _id;   

Account (String id)

{   _id = arg;   }   

}

Hide Method

When: A method is not used by any other class

Refactoring: Make the method private

Parameterize Method

When: Several methods do similar things but with different values contained in the method body. Then, Create one method that uses a parameter for the different values.

class Employee {

    void tenPercentRaise () {

        salary *= 1.1; }

    void fivePercentRaise () {

        salary *= 1.05;}

}

class Employee {

    void Raise (double factor) {

        salary *= (1+ factor); }

}

Replace Parameter with Explicit Method

When: a method that runs different cases depending on the values of a parameter. Then, create a separate method for each value of the parameter

void setValue (string name, int value) { if (name.equals("height")) height = value; if (name.equals("width")) width = value; }

void setHeight(int value) { height = value;}

void setWidth (int value) { width = value;}

Introduce Parameter Object

When: a group of parameters that naturally go together. Then , replace them with an object

Preserve Whole Object

When: You are getting several values from an object and passing these values as parameters in a method call. Then, send the whole object instead.

int low = daysTempRange.getLow();     

int high = daysTempRange.getHigh();     

withinPlan = plan.withinRange(low, high);  

withinPlan = plan.withinRange(daysTempRange);  

Replace Error Code with Exception

When: A method returns something to indicate an error is better performed with an Exception

bool withdraw(int amount) { if (amount > balance) return false; else { balance -= amount; return true; }}

void withdraw(int amount) throws BalanceException { if (amount > balance) { throw new BalanceException(); } balance -= amount;}

Replace Exception with Test

When: if you are catching an exception that could be handled by an if-statement, use that instead

int getValuebyIndex (int index) { try { return values[index]; } catch (ArrayIndexOutOfBoundsException e) { return 0; }}

int getValuebyIndex (int index) { if (index >= values.length) return 0; return values[index]; }

Replace Constructor with Factory Method

Factory Class: A utility class that creates an instance of a class from a

family of derived classes Useful in a situation that requires the creation of many

different types of objects, all derived from a common base type

Replace Constructor with Factory Method

Class Computer

{

………

};

class Laptop: public Computer

{

………

};

class Desktop: public Computer

{

......

};  

class ComputerFactory {

public:

static Computer *NewComputer(const std::string &description)

{

if(description == "laptop")

return new Laptop;

if(description == "desktop")

return new Desktop;

return NULL;

}

};  

Client Side:

Computer * lapobj = ComputerFactory::NewComputer(“laptop”);

Computer * deskobj = ComputerFactory::NewComputer(“desktop”);

Dealing with Generalization

Pull Up Field Pull Up Method Pull Up Constructor Body Push Down Method Push Down Field Extract Subclass Extract Superclass Extract Interface

Dealing with Generalization

Collapse Hierarchy Form Template Method Replace Inheritance with Delegation Replace Delegation with Inheritance

Method/Field Movement

Pull Up Method/Field When: Two subclasses have the same method/field Refactoring: Move the method/field to the superclass

Push Down Method/Field When: A method/field is only used in subclass Refactoring: move the method/field to the subclass

Method/Field Movement

Example: Push Down Method

Pull Up Constructor Body

When: constructors on subclasses with mostly identical bodies.

Refactoring: Create a superclass constructor; call this from the subclass

methods

Pull Up Constructor Body

class Employee{

private:

string name;

string id;

}

class Manager : public Employee{

private:

int grade;

public:

Manager (string n, string i, int g) {

name = n;

id = i;

grade = g;

}

}

class Employee{

private:

string name;

string id;

public:

Employee(string n , string I ){

name = n;

id = I;

}

}

class Manager : public Employee{

private:

int grade;

public:

Manager (string n, string i, int g) {

Employee (n, i);

grade = g;

}

}

Extract Subclass

When: A class has features that are used only in some instances. Then, create a subclass for that subset of features

Extract Subclass

class JobItem ...

public JobItem (int unitPrice, int quantity,

boolean isLabor, Employee employee) {

_unitPrice = unitPrice;

_quantity = quantity;

_isLabor = isLabor;

_employee = employee; }

public int getTotalPrice() {

return getUnitPrice() * _quantity;

}

public int getUnitPrice(){

return (_isLabor) ? _employee.getRate() : _unitPrice;

}

public int getQuantity(){

return _quantity;

}

public Employee getEmployee() {

return _employee;

}

private int _unitPrice;

private int _quantity;

private Employee _employee;

private boolean _isLabor;

class Employee...

public Employee (int rate) {

_rate = rate;

}

public int getRate() {

return _rate;

}

private int _rate;

class JobItem...

protected JobItem (int unitPrice, int quantity,

boolean isLabor) {

_unitPrice = unitPrice;

_quantity = quantity;

protected boolean isLabor() {

return false;

}

public int getUnitPrice(){

return _unitPrice;

}

class LaborItem extends JobItem

public LaborItem (int quantity, Employee employee) {

super (0, quantity, true);

_employee = employee;

}

protected boolean isLabor() {

return true;

}

public int getUnitPrice(){

return _employee.getRate();

}

Extract Superclass

When: several classes with similar features

class Employee { private: string name; string jobTitle; public: string GetName();}

class Student { private: string name; Course course;public: string GetName(); }

class Person { private: string name; public: string GetName();}

class Employee : public Person { private: string jobTitle; }

class Student : public Person { private: Course course; }

Extract Interface

When: several classes have part of their interface in common

Interface: is an empty shell, there are only the signatures (name / params / return type) of the methods. The methods do not contain anything. The interface can't do anything. It's just a pattern.

Extract Interface

double charge(Employee emp, int days) { int base = emp.getRate() * days; if (emp.hasSpecialSkill()) return base * 1.05; else return base;}

interface Billable { public int getRate(); public boolean hasSpecialSkill();}

class Employee implements Billable ...

double charge(Billable emp, int days) { int base = emp.getRate() * days; if (emp.hasSpecialSkill()) return base * 1.05; else return base;}

Collapse Hierarchy

When: A superclass and subclass are not very different

Refactoring Solution: Merge them together

Replace Inheritance with Delegation

When: A subclass uses only part of a superclasses interface or

does not want to inherit data. Refactoring Solution:

Create a field for the superclass, change methods to delegate to the superclass, and remove the subclassing

Replace Inheritance with Delegation

Example:

class MyStack extends Vector {

public void push(Object element) {

insertElementAt(element,0);

}

public Object pop() {

Object result = firstElement();

removeElementAt(0);

return result;

}

class MyStack {

private Vector _vector = new Vector();

public boolean isEmpty() {

return _vector.isEmpty();

}

public void push(Object element) {

_vector.insertElementAt(element,0);

}

public Object pop() {

Object result = _vector.firstElement();

_vector.removeElementAt(0);

return result;

}}

Big Refactoring

Tease Apart Inheritance Split an inheritance hierarchy that is doing two jobs at once

Convert Procedural Design to Objects Separate Domain from Presentation

GUI classes that contain domain logic Extract Hierarchy

Create a hierarchy of classes from a single class where the single class contains many conditional statements

Convert Procedural Design

Take each record type and turn it into a dumb data object with accessors

Take all procedural code and put it into a single class Take each long method and apply Extract Method and the

related factorings to break it down. As you break down the procedures use Move Method to move each one to the appropriate dumb data class

Continue until all behavior is removed from the original class

Convert Procedural Design

Example:

Refactoring: Limitations

Tentative list due to lack of experience Database

Database schema must be isolated, or schema evolution must be allowed

Changing Published Interfaces Interfaces where you do not control all of the source code that

uses the interface Must support both old and new interfaces Don't publish interfaces unless you have to

Refactoring: Tools

Smalltalk Refactoring Browser Development environment written in Smalltalk Allows for Smalltalk source code to transform Smalltalk source

code Comments as a first-class part of the language

XRefactory Allows standard refactorings for C++