Kevin Rutherford on Refactoring

"My guiding light is Kent Beck's rules of Simple Design

  • The code must first be correct (as defined by tests);
  • then it should be a clear statement of the design (what J.B.Rainsberger calls "no bad names");
  • then it should contain no duplication (of text, of ideas, or of responsibility);
  • and finally it must be the smallest code that meets all of the above.

It's time to stop refactoring when the code makes a reasonable stab at meeting those goals, and when any further changes would add no further benefit."

--from an interview about his refactoring book(with the best cover illustration ever for a programming book).

Refactoring Ruby Edition

An outline the refactoring book by Jay Fields, et al. This is my aide-mémoire for the Hogwarts-style names of the Spells and Smells and is unlikely to be of much use to anyone who hasn't read the book.

Code Smells

Duplicated Code
Long Method
Large Class
Long Parameter List
Divergent Change

each logic change req's coding changes only to parts of big class Extract Class

Shotgun Surgery

one logic change req's coding changes in many classes Move Method Move Field or Extract Class or Inline Class

Feature Envy

put things together that change together Move Method

Data Clumps
Primitive Obsession
Parallel Inheritance Hierarchies
  • make sure that instances of one hierarchy refer to instances of the other
  • so hierarchy in referring class disappears Move Method or Move Field
Speculative Generality

YAGNI violations (only tests call this code!) Collapse Hierarchy or Inline Class or Inline Module or Remove Parameter or Rename Method

Temporary Field

only used sometimes in its class Extract Class or Introduce Null Object or Replace Method with Method Object

Message Chains

chain of method calls on same object Hide Delegate or Extract Method

Middle Man

when everything is delegated, remove delegation Inline Method or Replace Delegation with Hierarchy

Alternative Classes with Different Interfaces

doing same thing in more than one method Rename Method or Move Method or Extract Module

Incomplete Library Class

Move Method

Data Class

a class with attributes but no methods Remove Setting Method or Hide Method or Encapsulate Collection or Move Method to get logic from user of class into class

Refused Bequest

only if subclass tries to block methods of super Replace Inheritance with Delegation

Comments

if they compensate for bad code. good ones say why not what Extract Method or Rename Method or Introduce Assertion

Repetitive Boilerplate

Introduce Class Annotation

Refactoring Spells

Composing Methods
Extract Method
Inline Method

when method body is clear enough move it out of method

Inline Temp

remove temp var by replacing references w/ the expression

Replace Temp with Query

means replace temp variable with method call returning same value

Replace Temp with Chain

when many method calls on same object, chain them

Introduce Explaining Variable

replace complex expression w/ temp var named well to explain expression logic

Split Temporary Variable

don't re-use temp var names for different purposes

Remove Assignments to Parameters

don't change what object a parameter points to

Replace Method with Method Object

make very long method w/many local vars into object w/instance vars where one of its attributes is the object it's acting on

Substitute Algorithm

rewrite ugly method with better algorithm

Replace Loop with Collection Closure Method

select, reject, map, inject, etc. instead of loops

Extract Surrounding Method

if only middle of method varies, combine into one method with block parameter

Introduce Class Annotation

like attr_reader, to make very common functions declarative

Introduce Named Parameter

make params into hash at end of param list. see pg 144 for Class.hash_initializer to load them all into instance vars

Remove Named Parameter

if it would be easier to understand as simple parameters

Remove Unused Default Parameter

if parameter never uses its default, remove it

Dynamic Method Definition

use define_method or def_each (pg 154) or instance_exec or class annotation for repetitive method definitions

Replace Dynamic Receptor with Dynamic Method Definition

use define_method rather than method_missing

Isolate Dynamic Receptor

isolate method_missing in its own class

Move Eval from Runtime to Parse Time

for speed, expand scope of string to be eval'd

Moving Features Between Objects
Move Method
Move Field
Extract Class

if some methods and some state

Inline Class

fold tiny class into class that uses it

Hide Delegate

replace mgr = john.department.manager w/ john.manager so person object has def manager; @department.manager; end

Remove Middle Man

replace mgr = john.manager w/ john.department.manager

Organizing Data
Self Encapsulate Field

use getter/setter rather than @var

Replace Data Value with Object

ex: phone num was primitive, needs to be collection of fields and methods

Change Value to Reference

ex: simple customer object different for each order, each customer needs to be a single object that is referenced by each order

Change Reference to Value

an immutable reference object could become a data object

Replace Array with Object

when each elem means something different

Replace Hash with Object

when each elem means something different

Change Unidirectional Association to Bidirectional

to couple 2 classes with pointer and backpointer (needs explicit test for backpointer)

Change Bidirectional Association to Unidirectional

remove backpointer

Change Magic Number to Symbolic Constant

ex PI = 3.14159

Encapsulate Collection

do not return a collection because it could be changed outside object: return copy of collection. do not have setter for collection: have add/remove

Replace Record with Data Class

class to serve as proxy between data record and rest of program

Replace Type Code with Polymorphism

most of class is based on checking type variable, so have different subclasses for each type (assumes type known at object creation)

Replace Type Code with Module Extension

lots of shared logic, some different by type so mixin diff module for each instance to hold different logic like this: def type_code=(X); extend(X); end

Replace Type Code with State/Strategy

lots of shared logic, some different by type so may need to change type at runtime where instance variable is a state/strategy object to hold the different logic

Replace Subclass with Fields

if subclass very simple move its logic into the class

Lazily Initialized Attribute

ex: def emails; @emails ||= []; end

Eagerly Initialized Attribute

ex: def initialize; @emails = []; end

Simplifying Conditional Expressions
Decompose Conditional

make multi-part conditional expression into method named after its purpose

Recompose Conditional

use || rather than ternary, use return rather than conditional

Consolidate Conditional Expression

make method of conditions if many conditions give same result

Consolidate Duplicate Conditional Fragments

extract any code duplicated in all branches of conditional

Remove Control Flag

use break or return for multiple exits from loop

Replace Nested Conditional with Guard Clauses

use return if x for special cases, then do standard case

Replace Conditional with Polymorphism

different classes with same method name rather than conditional

Introduce Null Object

if many checks for nil, make a new class (possibly a singleton) that accepts same messages as real one and returns appropriate value eg, MissingPerson or UnknownCustomer

Introduce Assertion

in an assertions module

Making Method Calls Simpler
Rename Method

when name does not express purpose

Add Parameter
Remove Parameter

if it's not used, that is

Separate Query from Modifier

separate methods for return value and change state

Parameterize Method

combine similar methods that vary by one or a few values into one method

Replace Parameter with Explicit Method

ex: switch.turn_on, switch.turn_off rather than switch.change_state(true/false)

Preserve Whole Object

send object as parameter, not its attributes

Replace Parameter with Method

if parameter just contains result of method call

Introduce Parameter Object

to combine many parameters into one object parameter

Remove Setting Method

if not used, that is

Hide Method

making it private

Replace Constructor with Factory Method

complex create method rather than initialize, possibly returns objects of different class

Replace Error Code with Exception

raise an exception rather than using own error codes

Replace Exception with Test

exceptions should be used only in exceptional cases

Introduce Gateway

between OO code and complex or non-OO API

Introduce Expression Builder

like a DSL for using the gateway

Dealing with Generalization
Pull Up Method

move it farther up the method call stack (to superclass, usually)

Push Down Method

move it lower on the method call stack (to classthat uses it)

Extract Module

if some methods and no state

Inline Module

fold tiny module into class that uses it

Extract Subclass

if some methods/state used only in some cases, and if delegation not a better idea

Introduce Inheritance

to share behavior of different classes

Collapse Hierarchy

if class and subclass very similar, combine them

Form Template Method

sequence of steps in superclass method, steps defined in subclass methods

Replace Inheritance with Delegation

esp if subclass of collection using few methods of collection, instead define those methods using Forwardable

Replace Delegation with Hierarchy

when too many methods are delegated

Replace Abstract Superclass with Module

when a class is never instantiated

Big Refactorings
Tease Apart Inheritance

if two sets of responsibilities, use two hierarchies and delegation

Convert Procedural Design to Objects

Make each record type a data object with attributes and no methods. Put all remaining procedural code into one class. Refactor.

Separate Domain from Presentation

make fat models

Extract Hierarchy

each special case becomes a subclass