Refactorings

University of Nantes – LS2N, France

Gerson Sunyé

Preamble

overview refactorings
Figure 1. Where are we?

Plan

  • Introduction

  • Refactoring Operations

  • When to Refactor

  • Why Refactor

    • Refactor to Improve Extensibility

    • Refactor to Improve Maintainability

  • Integrate Refactoring to Software Development Process

  • Automatization

  • Conclusion

Software Evolution Ideas

  • Software is never finished.

  • Software maintenance is just continuous development.

  • Consequence: code becomes complex and brittle

Why?

  • Original design is always inadequate.

  • Even good designers cannot:

    • get right the first time.

    • predict how the software will evolve.

    • understand correctly the problem domain and user requirements.

Software Evolution Basis

— Grow, don’t build software.

Evolutionary Software Development
— Fred Brooks

Evolutionary Software Development

evolutionary
  1. Prototyping

  2. Evolution

  3. Consolidation

Software Prototyping

evolutionary
  • Solidifies user requirements

  • Sketches the initial software design

Software Evolution

evolutionary
  • Adds new functionalities.

  • Determines expansion points (hot-spots).

Software Consolidation

evolutionary
Improves Design:
  • Corrects defects

  • Introduces new abstractions

Software Refactoring Definition

A program transformation that preserves the visible behavior.

The activity of improving the design of a software source code.

Simple example

Class rename

From:
public class Stuff {
    // (...)
}
To:
public class ConfigurationManager {
    // (...)
}

Another Example

Insert intermediate class

From:
public class Root {}

public class ConcreteA extends Root {}

public class ConcreteB extends Root {}
To:
public class Root {}

public class IntermediateClass extends Root {}

public class ConcreteA extends IntermediateClass {}

public class ConcreteB extends IntermediateClass {}

A More Complex Example

complex refactoring

Origins

  • Software maintenance and evolution

  • Software application development

  • Software framework development

Motivation

  • The eternal quest for code uniqueness

  • The code is read and modified more often than it is written

Difficulties

  • Understand an existing design is hard

  • Modify an existing design is even harder

  • Code changes may introduce errors, defeating the propose.

  • Results are not visible.

Difficulties (Cont.)

  • Every software project is under time pressure.

  • Developers are paid to add new features.

  • Refactoring can be very expensive.

Code Evolution Without Refactoring

  • Design becomes more corrupt and code becomes more brittle.

  • Changes become more expensive and more frequent and are made quickly and poorly.

Refactoring During Software Construction

  • Gradually change the code to get a healthy design.

  • Design is done continuously.

  • By building the system, we discover how to improve it.

Plan

  • Introduction

  • Refactoring Operations

  • When to Refactor

  • Why Refactor

    • Refactor to Improve Extensibility

    • Refactor to Improve Maintainability

  • Integrate Refactoring to Software Development Process

  • Automatization

  • Conclusion

Refactoring Operations

  • Simple source-to-source transformation with no visual effect.

  • Since refactoring operations are behavior-preserving, they can be composed and still preserve behavior.

  • Learning refactoring operations is similar to learning simple arithmetics.

Operation Preconditions

  • Each refactoring operation has a set of preconditions

  • That is, the conditions that one should respect to perform the transformation

For instance:
  • The precondition to the operation remove attribute is that the attribute is not used.

Basic Operations

  • Add entity

  • Remove entity

  • Rename entity

  • Move entity

  • Intra-method

  • Composite operations

Add Entity

  • Add an attribute (instance or class level)

  • Add a class

  • Add a method (instance or class level)

add entity

Remove Entity

  • Remove an attribute (instance or class level)

  • Remove a class

  • Remove a method (instance or class level)

public class Example {
    private String name;

    public neverUsedMethod() {
        doNothing();
    }
}

Rename Entity

  • Rename a variable

  • Rename an attribute (instance or class level)

  • Rename a class

  • Rename a method (instance or class level)

  • Change a method signature:

    • rename, permute, add, or remove arguments.

public class Example {
    private String maeby;

    public myMethod() {
        String maybe;

        this.call(maeby, me);
    }

    public call(String one, String other) {
        doSomething();
    }
}

Push-down (Specialization) or Pull-up (Generalization) property

attribute generalization
  • Attribute push-down or pull-up (instance or class level).

  • Method push-down or pull-up (instance or class level).

Move Property

Move attribute or method to another class

move property

Intra-Method

  • Extract code as method

  • Extract code as temporary variable

  • Inline method

  • Inline temporary variable

Before
int increment(int i) {
    return i+1;
}
void m() {
    int a;
    a = increment(a);
}
Inline increment()
void m() {
    int a;
    a = a + 1;
}

Composite Operations

  • Encapsulate attribute

  • Make attribute read-only

  • Extract interface from class

  • Extract inner class

  • Create template method

Before
class A {
    public int code;
}
Encapsulate Code
class A {
    private int code;
    public int getCode() {return code;}
    public void setCode(int i) {code = i;}
}

Other Operations

  • Change entity visibility

  • Introduce factory method

  • Convert variable to attribute

Before
class A {
    public A(int i, String s, float f) {}
}
Introduce Factory Method
class A {
    private A(int i, String s, float f) {}

    public static createA(int i, String s, float f) {
        return new A(i, s, f);
    }
}

Plan

  • Introduction

  • Refactoring Operations

  • When to Refactor

  • Why Refactor

    • Refactor to Improve Extensibility

    • Refactor to Improve Maintainability

  • Integrate Refactoring to Software Development Process

  • Automatization

  • Conclusion

When to Refactor

Strategies During Software Evolution:
  1. Extend and then refactor

  2. Refactor to extend

  3. Debug and then refactor

  4. Refactor to debug

  5. Refactor to understand

Extend and then Refactor

When you need to add a new functionality:
  1. Find a class or method with similar behavior and copy it

  2. Make it work

  3. Eliminate redundancy

Refactor to Extend

A functionality seems too complex to implement:
  1. Refactor the current design to make the change easy

  2. Make the change

Debug and then Refactor

  • Locate and fix the bug

Refactor the code to make the bug obvious:
  • Add assertions

  • Extract method

  • Assign meaningful names

  • Create explaining constants for magic numbers

  • Create explaining constants or variables for complex expressions

Refactor to Debug

Since refactor operations are behavior-preserving, they also preserve bad behavior.
  • Before debugging, refactor to simplify complex code

  • Then, debug it

Refactor to Understand

Refactor when trying to understand a complex code:
  • Split large methods

  • Create explaining constants or variables for magic numbers

  • Assign meaningful names

  • Do not worry about performance

Plan

  • Introduction

  • Refactoring Operations

  • When to Refactor

  • Why Refactor

    • Refactor to Improve Extensibility

    • Refactor to Improve Maintainability

  • Integrate Refactoring to Software Development Process

  • Automatization

  • Conclusion

Refactor for Extensibility

Strategy:
  • Refactor code before code extension

  • Separate things that change from things that do not.

  • Apply design patterns.

Design Patterns

Variability pointDesign Pattern

Algorithms

Strategy, Visitor

Actions

Command

Implementation

Bridge

Response to change

Observer

Interactions between objects

Mediator

Variability pointDesign Pattern

Object being created

Factory Method, Abstract Factory, Prototype

Structure being crated

Builder

Traversal Algorithm

Iterator

Object Interfaces

Adapter

Object Behavior

Decorator, State

Example: Introduce Algorithm Variability

public class Client {
    public void writeAsciiOn(OutputStream o) {
        o.print('name: ');
        o.print(this.name);
        (...)}
    }

Suppose that we want to print in HTML, XML, JSON, etc.

Steps

  1. Create AsciiStrategy class

  2. Add instance attribute to class Client and initialize it to AsciiStrategy

  3. Move method writeAsciiOn()`to class `AsciiStrategy

  4. Rename method writeAsciiOn() to writeOn()

Result

public class Client {
    private WriteStrategy writeStrategy = new AsciiStrategy();
    public void writeOn(OutputStream o) {
        writeStrategy.writeOn(this, o)}
}

public class AsciiStrategy {
    public void writeOn(Client c, OutputStream o)
        o.print('name: ');
        o.print(c.name);
        (...)}
}

Consequences

  • The printing behavior was extracted from the class Client.

  • Adding new printing behavior could be easily achieved.

write strategy

Plan

  • Introduction

  • Refactoring Operations

  • When to Refactor

  • Why Refactor

    • Refactor to Improve Extensibility

    • Refactor to Improve Maintainability

  • Integrate Refactoring to Software Development Process

  • Automatization

  • Conclusion

Refactor to Improve Code Maintainability

Strategy:
  • Use Code Smells to find where the code should be improved

Code Smells

  • Long Method

  • Large Class

  • Long Parameter List

  • Nested Conditionals

  • Parallel Inheritance Hierarchies

  • Duplicated Code

  • Speculative Generality

Long Method

Fix
  • Extract code snippets as smaller methods:

    • If an entire method is long and low-level, find the sequence of higher-level steps.

    • Comments in the middle of a method often point out good places to extract.

  • Smaller methods can often be reused

Large Class

Fix
  • Create compositions of smaller classes

  • Find logical sub-components of the original class and create classes to represent them

  • Move methods and attributes into the new components

  • Related refactoring operations: Extract Class, Extract Subclass

Long Parameter List

Fix
  • Create a class containing all interrelated parameters.

  • Use this class as a parameter

  • Find methods that should be in the new class

Nested Conditionals

Fix
  • If the conditional expression involves type test (isKindOf(), type(), getClass(), etc.), put the method on that class.

  • If the expression involves null objects (isEmpty(), isNil(), null, empty()), consider the Null Object pattern.

Parallel Inheritance Hierarchies

Fix
  • Use Move Method and Move Attribute to combine the hierarchies into one.

Duplicated Code

Fix
  • Push-up identical methods to common superclass

  • Push-up the more general method

  • Move the method into a common component (e.g., Strategy)

  • Related refactoring operations: Extract Method, PullUp Method, Form Template Method

Speculative Generality

Fix
  • Remove unnecessary delegation with Inline Class

  • Remove (almost) empty abstract classes

  • Remove unused parameters

  • Methods named with odd abstract names should be brought down to earth with Rename Method

Plan

  • Introduction

  • Refactoring Operations

  • When to Refactor

  • Why Refactor

    • Refactor to Improve Extensibility

    • Refactor to Improve Maintainability

  • Integrate Refactoring to Software Development Process

  • Automatization

  • Conclusion

The Loan Metaphor

  • Quick and dirty coding is like taking out a loan

  • Living with bad code is the interest

  • Debt is necessary for a business

  • Too much debt is not healthy and will eventually catch up to you

Refactoring Phase

Refactor after a code release:
  • Little more breathing room

  • The design is still fresh in your mind

Agile Software Development

  • Listen

  • Test

  • Code

  • Refactor Continually

Agile Software Development (Cont.)

  • Some of the principles still apply, even if your not extreme

  • Whenever something seems difficult or awkward, refactor to make it easy

  • Let the program tell you where it needs to be fixed

  • If you cut and paste, you must refactor

Plan

  • Introduction

  • Refactoring Operations

  • When to Refactor

  • Why Refactor

    • Refactor to Improve Extensibility

    • Refactor to Improve Maintainability

  • Integrate Refactoring to Software Development Process

  • Automatization

  • Conclusion

Using Standard Tools

  • Safe refactoring needs tests

  • Tests must pass before and after each refactoring operation.

  • Use standard testing tools: JUnit, TestNG, etc.

  • Current Java IDE provide refactoring operations: Eclipse, Netbeans, IntelliJ IDEA

Refactoring Tools

Smalltalk

Refactoring Browser, Lint.

Python

Bicycle Repair Man, pycheck.

Java

Eclipse, IntelliJ Idea, JFactor, XRefactory, JBuilder, RefactorIt, JRefactory, Transmogrify, JafaRefactor, CodeGuide, jLint.

C++

SlickEdit, Ref++, Xrefactory.

Ruby

Ruby Refactoring

Plan

  • Introduction

  • Refactoring Operations

  • When to Refactor

  • Why Refactor

  • Refactor to Improve Extensibility

  • Refactor to Improve Maintainability

  • Integrate Refactoring to Software Development Process

  • Automatization

  • Conclusion

Conclusion

  • Evolutionary Software Development

  • Refactoring operations

  • Ways of integrating refactoring into your process

References

Credits

Several slides and examples are based on a John Brant and Don Roberts presentation "Refactoring Techniques and Tools" at Smalltalk Solutions '99.