Skip to content

Instantly share code, notes, and snippets.

@alvinncx
Last active August 17, 2021 16:13
Show Gist options
  • Save alvinncx/96fa0d75b972bd6c5987137b79a3283c to your computer and use it in GitHub Desktop.
Save alvinncx/96fa0d75b972bd6c5987137b79a3283c to your computer and use it in GitHub Desktop.
Refactoring by Martin Fowler

Principles in Refactoring

Purpose of refactoring

  • Make the software easier to understand
  • Does not change observable behavior

Difficult to change

  • Programs that are hard to read are hard to modify.
  • Programs that have duplicated logic are hard to modify.
  • Programs that require additional behavior that requires you to change running code are hard to modify.
  • Programs with complex conditional logic are hard to modify.

Two hats

  • Refactoring hat
    • Restructing code, checking tests pass
  • Adding features

When to do refactoring?

Refactoring is something you do in small bursts. Refactor when you see a bug, because the code wasn't cleare enough to detect a bug.

I know enough to do today's work. I don't know enough to do tomorrow's. But if I only work for today, I won't be able to work tomorrow at all.

But what about indirection?

Most refactoring introduces more indirection. But indirection can be useful when

Sharing logic
  • submethod that is invoked in 2 places
  • superclass shared by classes
Explain intention and implementation separately
  • choose the name to explain what you intend
Isolate change
Encode conditional logic
  • by changing explicit conditionals to messages, you can reduce duplication, add clarity and increase flexibility
  • messages as in polymorphic messages

How to identify where to change

  • Add indirection without changing behavior to current code.
  • Remove parasitic indirection when it no longer serves its purpose.

When to avoid refactoring

  • When it is easier to rewrite
  • When meeting deadlines
    • When not meeting deadlines, seek to refactor before it is too late

Refactoring and performance

Most program waste time in a small fraction of the code. If you optimise all the code, then 90% of optimisations are wasted.

  • The time spent to make the program fast, the time lost becuase of lack of clarity, is wasted time.

Code smells

Duplicated code

  • Same method, in same class
    • Extract method
  • Same method, in 2 sub class
    • Extract method, pull up field
    • Extract method, form template method
    • Substitute algorithm
  • Same method, in 2 unrelated class
    • Extract class

Long Methods

Longer methods / procedures are difficult to read.

Be more aggressive in decomposing methods

  • if you feel like writing a comment, write a method.
  • Extract method

Long parameter lists and temp variables

  • Replace temp with Query
  • Introduce parameter object, Preserve whole object
  • Replace method with method object
  • Decompose conditional

Large Class

Too many instance variables

  • Extract class, Extract subclass Study how clients use the class
  • Extract interface

Divergent changes

One class suffers from many changes. Find commonality between changes

  • Extract class

Shotgun Surgery

One change alters many classes. Changes are all over the place

  • Move method and move field
  • Inline class

Feature envy

Method invokes a lot of methods from another class just to calculate

  • Move method, extract method
  • Self delegation
  • Strategy and Visitor

Data clumps

Bunches of data appear as fields

  • Extract class on fields
  • Introduce parameter object, Preserve Whole object

Primitive Obsession

Don't purely rely on primitive types of your language Use small objects for small tasks

  • e.g. Money class for number and currency
  • e.g. Special class for telephone and zipcodes
  • Replace data with object on individual data values
  • If data is type code, Replace with Class
  • If conditionals depend on type code,
    • use Replace Type code with Subclasses or
    • Replace Type code with State/Strategy
  • If group of fields go together, extract class
  • If primitives in parameter lists, Introduce parameter object
  • If picking apart an array, Replace Array with object

Swtich statements

If switch statement is created, it will likely be created elsewhere

  • Extract method on the switch statemetn and then Move method into the class where polymorphism is needed.
  • Then use Replace Type code with Subclass or Replace Type code with State/Strategy
  • Finally can use Replace Conditional with Polymorphism
  • If it is one off, maybe use Replace Parameter with Explicit Methods
  • If one of it is numm, can Introduce Null Object

Parallel Inheritance Hierarchies

  • Subclasing one also causes you to subclass the other in order to work
  • Move method or Move field.

Lazy class

Classes that have shrunk, but no longer relevant

  • Collapse Hierarchy, Inline class

Speculative Generality

Created but never used

  • Collapse Hierarchy
  • Remove delegation with Inline Class
  • Remove parameter
  • If methods no longer sound right, Rename Method

Temporary Field

Instance variables that are set sometimes

  • Extract class for orphaned variables
  • Use Null Object to handle alternative path when variables are not valid

Message Chaining

Object calls another object, which calls another object

  • Extract method, and use method to push down the chain
  • Hide delegate

Middleman syndrome

Many methods are delegated to the other class

  • Remove middle man, talk to real object
  • Inline method
  • Replace delegation with inheritance

Inappropriate intimacy

Classes should not call each other's private methods

  • Use move method and move field to separate
  • Change bidirectional association to unidirectional
  • Extract class and put commonality together
  • Hide delegate
  • For subclasses, Replace Delegration with Inheritance

Alternative classes with different interface

  • Rename methods that so same thing but different signatures
  • Move method and Extract superclass

Incomplete Library class

  • Introduce foreign methods
  • Introduce local extension

Data class

Dumb data classes manipulated too much by other classes due to public fields

  • Encapsulate field
  • Encapsulate collection
  • Remove setting
  • Move or extract methods into data class as behaviors where getter and setters are used
  • Hide methods

Refused Bequest

Subclasses don't use methods and data of parents

  • Create new sibling and push down methods and field Worst, subclass use methods but doens't not want to support behavior of superclass, breaking inheritance
  • Replace inheritance with delegation.

Comments

If comments are needed, extract method If extracted method needs explanation, rename method If rules are needed, introduce assertion

Building tests

When you need a feature, start by writing tests.

When writing tests, start by editing applicationc code to make it fail

  • Check that the test actually
  • Check that the test is actually testing what it needs to test

Testing approaches

  1. Testing every public interface
  • It doesn't make sense to test every public method
  • e.g. Getters and Setters are so simple, it is unlikely to have a bug
  1. Testing that is risk driven
  • The key to testing is to test the areas that you are most worried about going wrong.
  • Test boundary conditions
  • Test nulls, test empty

You cannot prove a program has no bugs by testing The idea of testing is to speed up programming

Testing has a point of diminishing returns

There is a danger with writing too many tests. Concentrate on where the risk is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment