Refactoring

39
Refactoring: A Brief Introduction

Transcript of Refactoring

Page 1: Refactoring

Refactoring: A Brief Introduction

Page 2: Refactoring

Refactoring

Refactoring(noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.

Refactor(verb): to restructure existing software by applying a series of refactorings without changing its observable behavior.

Page 3: Refactoring

An Example with NUnit

#region Usingusing NUnit.Framework;#endregion

namespace com.sds.prime{ [TestFixture] public class PrimeTest { [Test] public void testMethod() { Prime prime = new Prime(); } }}

Page 4: Refactoring

An Example with NUnit

using System;

namespace com.sds.prime{ public class Prime { public Prime() { } }}

Page 5: Refactoring

Run Test with NUnit plugin

Page 6: Refactoring

Run Test with NUnit plugin

------ Test started: Assembly: PrimeNumber.exe ------

1 succeeded, 0 failed, 0 skipped, took 0.05 seconds.

---------------------- Done ----------------------

Page 7: Refactoring

An Example with NUnit

#region Usingusing NUnit.Framework;#endregion

namespace com.sds.prime{ [TestFixture] public class PrimeTest { [Test] public void testMethod() { Prime prime = new Prime(); Assert.IsTrue(prime.getNext(1) == 1); } }}

Page 8: Refactoring

An Example with NUnit

using System;

namespace com.sds.prime{ public class Prime { public Prime() { }

public int getNext(int a_numberOfPrimes) { return 1; } }}

Page 9: Refactoring

Run Test with NUnit plugin

Page 10: Refactoring

Run Test with NUnit plugin

------ Test started: Assembly: PrimeNumber.exe ------

1 succeeded, 0 failed, 0 skipped, took 0.05 seconds.

---------------------- Done ----------------------

Page 11: Refactoring

An Example with NUnit

#region Usingusing NUnit.Framework;#endregion

namespace com.sds.prime{ [TestFixture] public class PrimeTest { [Test] public void testMethod() { Prime prime = new Prime(); Assert.IsTrue(prime.getNext(1) == 1); Assert.IsTrue(prime.getNext(2) == 2); } }}

Page 12: Refactoring

An Example with NUnit

using System;

namespace com.sds.prime{ public class Prime { public Prime() { }

public int getNext(int a_numberOfPrimes) { if (a_numberOfPrimes== 1) return 1; if (a_numberOfPrimes == 2) return 2; } }}

Page 13: Refactoring

Run Test with NUnit plugin

Page 14: Refactoring

Run Test with NUnit plugin

------ Test started: Assembly: PrimeNumber.exe ------

1 succeeded, 0 failed, 0 skipped, took 0.05 seconds.

---------------------- Done ----------------------

Page 15: Refactoring

An Example with NUnit

#region Usingusing NUnit.Framework;#endregion

namespace com.sds.prime{ [TestFixture] public class PrimeTest { [Test] public void testMethod() { Prime prime = new Prime(); Assert.IsTrue(prime.getNext(1) == 1); Assert.IsTrue(prime.getNext(2) == 2); Assert.IsTrue(prime.getNext(10) == 23); } }}

Page 16: Refactoring

An Example with NUnitpublic int getNext(int a_numberOfPrimes){ if (a_numberOfPrimes== 1) return 1; if (a_numberOfPrimes == 2) return 2;

int primeCounter = 2; for (int candidate=3; candidate < 1000; candidate++) { if (isPrime(candidate)) { primeCounter++; if (primeCounter == a_numberOfPrimes) return candidate; } }

return 0;}

Page 17: Refactoring

An Example with NUnitprivate bool isPrime(int a_potentialPrime){ bool isPrime = true;

for (int potentialDivisor = 2; potentialDivisor < a_potentialPrime; potentialDivisor++) { if (a_potentialPrime % potentialDivisor == 0) { isPrime = false; break; } } return isPrime;}

Page 18: Refactoring

Run Test with NUnit plugin

Page 19: Refactoring

Run Test with NUnit plugin

------ Test started: Assembly: PrimeNumber.exe ------

1 succeeded, 0 failed, 0 skipped, took 0.05 seconds.

---------------------- Done ----------------------

Page 20: Refactoring

An Example with NUnitpublic int getNext(int a_numberOfPrimes){ if (a_numberOfPrimes== 1) return 1; if (a_numberOfPrimes == 2) return 2;

int primeCounter = 2; for (int candidate=3; candidate < 1000; candidate+2) { if (isPrime(candidate)) { primeCounter++; if (primeCounter == a_numberOfPrimes) return candidate; } }

return 0;}

Page 21: Refactoring

An Example with NUnitprivate bool isPrime(int a_potentialPrime){ bool isPrime = true; int halfWay = a_potentialPrime / 2;

for (int potentialDivisor = 2; potentialDivisor < halfWay; potentialDivisor++) { if (a_potentialPrime % potentialDivisor == 0) { isPrime = false; break; } } return isPrime;}

Page 22: Refactoring

Run Test with NUnit plugin

Page 23: Refactoring

Run Test with NUnit plugin

------ Test started: Assembly: PrimeNumber.exe ------

1 succeeded, 0 failed, 0 skipped, took 0.05 seconds.

---------------------- Done ----------------------

Page 24: Refactoring

Testing Boundary Conditions

As developers, we generally code with the core of the requirement in mind. We test the ‘common’ scenarios and mark it complete. However, most of our code breaks around our boundary conditions.

Page 25: Refactoring

Refactoring

Now back to Refactoring.

An Example …

Page 26: Refactoring

Smells

A smell is a warning sign about a potential problem.

Page 27: Refactoring

Code Smells

Common smells in our code• Comments• Long Methods• Large Class• Long Parameter List• Inappropriate Intimacy• Duplicated Code• Conditional Complexity

A list of descriptions can be found here

http://www.codinghorror.com/blog/archives/000589.html

Page 28: Refactoring

Smell:Comments

When a comment explains a block of code, use Extract Method to pull the block out into a properly named method.

When a comment explains what a method does, use Rename Method to give the method a more descriptive name.

Payoff: Improves communication, may expose duplication.

Page 29: Refactoring

Smell:Long Methods

Use Extract Method to break up the method into smaller pieces.

Payoff: Improves communication, may expose duplication, often helps new classes and abstractions emerge.

Page 30: Refactoring

Smell:Large Class

Use Extract Class if you can identify a new class that has part of this class’s responsibilities.

Use Extract Subclass if you can divide responsibilities between the class and a new subclass.

Use Extract Interface if you can identify subsets of features that clients use.

Payoff: Improves communication, may expose duplication.

Page 31: Refactoring

Smell:Long Parameter List

If the parameter value can be obtained from another object this

one already knows use Replace Parameter with Method.

If the data is not from one logical object, you may be able to

group them using Introduce Parameter Object.

Payoff: Improves communication, may expose duplication, often reduces size.

Page 32: Refactoring

Smell:Inappropriate Intimacy

If two independent classes are entangled, use Move Method

and Move Field to put the right pieces in on the right class.

If the tangled part seems to be a missing class, use Extract Class and Hide Delegate to introduce the new class.

Payoff: Reduces duplication, often improves communication, may reduce size.

Page 33: Refactoring

Design Patterns

Design patterns are used in Object Oriented (OO) programming to create structured, adaptive classes. Refactoring ultimately results in some kind of design pattern.

Page 34: Refactoring

Template Pattern

MyClass::someMethod() {

aaa aaa aaa aaabb bb bb bb bb cccc cccc cccc d d d d d d d d deee eee eee eee fffff ff fffff ffggg gggg ggg h hh h hh h hh hiiii iiii iiii iiii

}

Design Patterns Explained, ‘Chapter 19 : The Template Method Pattern’, by Alan Shalloway and James R. Trott

Page 35: Refactoring

Template Pattern

Using copy and paste to create new code from existing code results in redundancies

aaa aaa aaa aaabb bb bb bb bb cccc cccc cccc d d d d d d d d deee eee eee eee fffff ff fffff ffggg gggg ggg h hh h hh h hh hiiii iiii iiii iiii

AAA A AAAA AXXX XX XX XBB BB B cccc cccc cccc DDD D DDD DEE EEEE E E fffff ff fffff ffGGG G G GGGG HHH HHH HH Hiiii iiii iiii iiii

Design Patterns Explained, ‘Chapter 19 : The Template Method Pattern’, by Alan Shalloway and James R. Trott

Page 36: Refactoring

Template Pattern

MyClass::someMethod() {

methodForStep1()

cccc cccc cccc

methodForStep3()

fffff ff fffff ff

methodForStep5()

iiii iiii iiii iiii }

Design Patterns Explained, ‘Chapter 19 : The Template Method Pattern’, by Alan Shalloway and James R. Trott

methodForStep1() {

aaa aaa aaa aaa

bb bb bb bb bb

}

methodForStep3() {

d d d d d d d d d

eee eee eee eee

}

methodForStep5() {

ggg gggg ggg

h hh h hh h hh h

}

Page 37: Refactoring

Template Pattern

BaseClass::someMethod() {

methodForStep1()

cccc cccc cccc

methodForStep3()

fffff ff fffff ff

methodForStep5()

iiii iiii iiii iiii }

Design Patterns Explained, ‘Chapter 19 : The Template Method Pattern’, by Alan Shalloway and James R. Trott

MyClass {

methodForStep1() {

aaa aaa aaa aaa

bb bb bb bb bb

}

methodForStep3() {

d d d d d d d d d

eee eee eee eee

}

methodForStep5() {

ggg gggg ggg

h hh h hh h hh h

}

}

MySecondClass {

methodForStep1() {

AAA A AAAA A

XXX XX XX X

BB BB B

}

methodForStep3() {

DDD D DDD D

EE EEEE E E

}

methodForStep5() {

GGG G G GGGG

HHH HHH HH H

}

}

Page 38: Refactoring

Prime Number Template

We could easily abstract our Prime class using the Template pattern so that we can handle different algorithms of the Prime number generator.

Page 39: Refactoring

Resources

“Test Patterns Explained” by Alan Shalloway, James R. Trott“Refactoring” by Martin Fowler, www.refactoring.com“Pragmatic Unit Testing in C# with Nunit” by Andrew Hunt, David Thomas“The Pragmatic Programmer” by Andrew Hunt, David Thomas

Code Smells, http://www.codinghorror.com/blog/archives/000589.htmlXunit Frameworks, http://www.opensourcetesting.org/unit_misc.phpTestDriven VS Plugin, http://www.testdriven.net/

[email protected]