9. refactoring

39
© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.1 9. Refactoring Refactoring What is it? Why is it necessary? Examples Tool support Refactoring Strategy Code Smells Examples of Cure Refactoring and Reverse Engineering Refactor to Understand Tool Demonstrations Smalltalk Java Conclusion

Upload: frances-woods

Post on 04-Jan-2016

69 views

Category:

Documents


0 download

DESCRIPTION

9. Refactoring. Refactoring What is it? Why is it necessary? Examples Tool support Refactoring Strategy Code Smells Examples of Cure Refactoring and Reverse Engineering Refactor to Understand Tool Demonstrations Smalltalk Java Conclusion. The Reengineering Life-Cycle. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.1

9. RefactoringRefactoring

What is it? Why is it necessary? Examples Tool support

Refactoring Strategy Code Smells Examples of Cure

Refactoring and Reverse Engineering Refactor to Understand

Tool Demonstrations Smalltalk Java

Conclusion

Page 2: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.2

The Reengineering Life-Cycle

Requirements

Designs

Code

(0) requirementanalysis

(1) modelcapture

(2) problemdetection (3) problem

resolution

(4) program transformation

(4) program transformationissues• Tool support• Failure proof

Page 3: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.3

What is Refactoring?“The process of changing a software system in such a way that it does not alter the external behaviour of the code, yet improves its internal structure” [Fowl99a]

“A behaviour-preserving source-to-source program transformatio”n [Robe98a]

“A change to the system that leaves its behaviour unchanged, but enhances some non-functional quality - simplicity, flexibility, understandability, …” [Beck99a]

Page 4: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.4

Typical RefactoringsClass Refactorings

Method Refactorings Attribute Refactorings

add (sub)class to hierarchy

add method to class add variable to class

rename class rename method rename variable

remove class remove method remove variable

push method down push variable down

push method up push variable up

add parameter to method create accessors

move method to component

abstract variable

extract code in new method

These simple refactorings can be combined to provide bigger restructurings such as the introduction of design patterns.

Page 5: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.5

Why Refactoring?“Grow, don’t build software” Fred Brooks

Some argue that good design does not lead to code needing refactoring, but in reality

Extremely difficult to get the design right the first time You cannot fully understand the problem domain You cannot understand user requirements, if the user does! You cannot really plan how the system will evolve in five

years Original design is often inadequate System becomes brittle, difficult to change

Refactoring helps you to Manipulate code in a safe environment (behavior

preserving) Recreate a situation where evolution is possible Understand existing code

Page 6: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.6

Refactoring and OOObject-Oriented Programming

emphasize the possibility of changes

rapid development cycle incremental definition

Frameworks family of products from the

same skeletons or kernel reuse of functionality

However software evolves, grows and...

dies if not taken care of

This is where refactoring comes in

New / ChangingRequirements

MoreReuse

EXPANSIONCONSOLI-DATION

Iterative development

Consolidation is necessary to ensure next expansion success

Page 7: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.7

Examples of Refactoring Analysis

AddClasssimple namespace use and static references

between class structure

Rename Methodexistence of similar methodsreferences of method definitionsreferences of calls

Page 8: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.8

Add Class

Add Class (new name, package, superclasses, subclasses)

Preconditions no class or global variable exists with new name in the same scope subclasses are all subclasses of all superclasses [Smalltalk] superclasses and subclasses cannot be metaclasses

Postconditions new class is added into the hierarchy with superclasses as

superclasses and subclasses as subclasses new class has name new name subclasses inherit from new class and not anymore from

superclasses

Considerations: Abstractness

B

C D FN

A B

C D

F

A

Page 9: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.9

Rename Method: Do It Yourself

• Do it yourself approach• Check if a method does not exist in the class and

superclass/subclasses with the same “name”• Browse all the implementers (method definitions)• Browse all the senders (method invocations)• Edit and rename all implementers• Edit and rename all senders• Remove all implementers• Test• Automated refactoring is better !

BX

B b = new B();b.blnc();

blnce()

Ablnce()

Dblnce()

Cblnce()

BXbalance()

Abalance()

Dbalance()

C

balance()

B b = new B();b.balance();

Page 10: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.10

Rename Method (method, newname)

Preconditions no method exists with the signature implied by new

name in the inheritance hierarchy that contains method [Smalltalk] no methods with same signature as method

outside the inheritance hierarchy of method [Java] method is not a constructor

Postconditions method has new name relevant methods in the inheritance hierarchy have new

name invocations of changed method are updated to new

name

Other Considerations Statically/Dynamically Typed Languages

=> Scope of the renaming

Page 11: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.11

Which Refactoring Tools?Change Efficient

Refactoring Source-to-source program

transformation Behaviour preserving

=> improve the program structure

Programming Environment Fast edit-compile-run

cycles Integrated into your

environment Support small-scale

reverse engineering activities

=> convenient for “local” ameliorations

Failure ProofRegression Testing

Repeating past tests Tests require no user

interaction Tests are deterministic Answer per test is yes / no

=> verify if improved structure does not damage previous work

Configuration & Version Management keep track of versions that

represent project milestones

=> possibility to go back to previous version

Page 12: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.12

Some Tools

• Refactoring Browser (in VisualWorks Smalltalk, Dolphin, Squeak, IBMSmalltalk since 1995)http://www.cincom.com/smalltalk

• Java Refactorings in Eclipsehttp://www.eclipse.org(also available in other Java environments)

Page 13: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.13

Top Ten of Code Bad Smells (i)

"If it stinks, change it" Grandma Beck

• Duplicated Code• Long Method• Large Class (Too many responsibilities)• Long Parameter List (Object is missing)• Case Statement (Missing polymorphism)• Divergent Change (Same class changes

differently depending on addition)• Shotgun Surgery (Little changes distributed

over too many objects)

Page 14: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.14

Top Ten of Code Bad Smells (ii)

• Feature Envy (Method needing too much information from another object)

• Data Clumps (Data always use together (x,y -> point))

• Parallel Inheritance Hierarchies (Changes in one hierarchy require change in another hierarchy)

• Lazy Class (Does not do too much)• Middle Man (Class with too many delegating

methods)• Temporary Field (Attributes only used under very

specific circumstances)• Message Chains (Coupled classes, internal

representation dependencies) • Data Classes (Only accessors)

Page 15: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.15

Unit Testing• How can I trust that the changes

did not destroy something?• What is my confidence in the

system?

Page 16: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.16

Tests• Tests represent your trust in the system• Build them incrementally

Do not need to focus on everythingWhen a new bug shows up, write a test

• Even better write them before the codeAct as your first client, better interface

• Active documentation always in sync

Page 17: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.17

Testing Style“The style here is to write a few lines of code, then a test that should run, or even better, to write a test that won't run, then write the code that will make it run.”

• write unit tests that thoroughly test a single class• write tests as you develop (even before you implement)• write tests for every new piece of functionality

“Developers should spend 25-50% of their time developing tests.”

Page 18: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.18

But I can’t cover everything!

• Sure! Nobody can but• When someone discovers a defect in your

code, first write a test that demonstrates the defect. Then debug until the test succeeds.

“Whenever you are tempted to type something into a print statement or a debugger expression, write it as a test instead.”Martin Fowler

Page 19: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.19

Good Tests• Repeatable• No human intervention• “self-described”• Change less often than the system• Tells a story

Page 20: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.20

JUnitJunit (inspired by Sunit) is a simple “testing framework”

that provides:• classes for writing Test Cases and Test Suites• methods for setting up and cleaning up test data

(“fixtures”)• methods for making assertions • textual and graphical tools for running tests

JUnit distinguishes between failures and errors:• A failure is a failed assertion, i.e., an anticipated

problem that you test.• An error is a condition you didn’t check for.

Page 21: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.21

The JUnit Framework

Page 22: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.22

A Testing Scenario

The framework calls the test methods that you define for your test cases.

Page 23: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.23

Example: Testing Set• Class: SetTestCase

superclass: TestCase instance variable: empty full

• SetTestCase>>setUp empty := Set new. full := Set with: #abc with: 5

• The setUp is the context in which each test is run.

Page 24: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.24

Tests…SetTestCase>>testAdd

empty add: 5. self assert: (empty includes: 5).

SetTestCase>>testOccurrences self assert: (empty occurrenceOf: 0) = 0. self assert: (full occurrencesOf: 5) = 1. full add: 5. Self assert: (full occurrencesOf: 5) = 1

SetTestCase>>testRemove full remove: 5. self assert: (full includes: #abc). self deny: (full includes: 5)

Page 25: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.25

Test and Refactorings• Tests can cover places where you

have to manually change the codeChanging 3 by 33, nil but NewObject

new

• Tests let you been more aggressive to change and improve your code

Page 26: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.26

Refactoring and Reverse Engineering

Requirements

Designs

Code

(0) requirementanalysis

(1) modelcapture

(2) problemdetection (3) problem

resolution

(4) program transformation

(1) Model capture

issues• Tool support• (Failure proof)

Page 27: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.27

Refactor To UnderstandObvious

• Programs hard to read => Programs hard to understand => Programs hard to modify

• Programs with duplicated logic are hard to understand• Programs with complex conditionals are hard to

understand• Programs hard to modify

Refactoring code creates and supports the understanding

• Renaming instance variables helps understanding methods

• Renaming methods helps understanding responsibility

• Iterations are necessary The refactored code does not have to be used!

Page 28: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.28

Case Study: Refactor to Understand

We want to know if a tool that analyses source code extracts the right information.Context:

• parser creates objects representing the source code entities

• parameterised by level of detail• 0: classes, methods, ...• 4: classes, methods, attributes, accesses,

invocations, …Problem: • We need to understand the way entities are

created• ... but the code is unclear and full of

duplication.

Page 29: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.29

ExampleclassWithMethodsOnly: aSmalltalkClass isMetaClass: isMetaClass

classDefName := self assessClassNameFor: aSmalltalkClass isMetaClass: isMetaClass. classDef := self makeFClassNamed: classDefName. self assessClassAttributesFor: classDef smalltalkClass: aSmalltalkClass isMetaClass:

isMetaClass. "--inheritance--" (self haveExplicitMetaClasses not & isMetaClass) ifFalse: [aSmalltalkClass superclass isNil

ifFalse: [ | superclassDefName |"Check for the occasion of a nil subclass"

superclassDefName := self assessClassNameFor: aSmalltalkClass superclassisMetaClass: isMetaClass.

self makeFInheritanceSuperclass: superclassDefNamesubclass: classDefName]].

(self haveExplicitMetaClasses not & isMetaClass)ifTrue: [classDefName := (classDefName, '_class') asSymbol].

"--method-definitions--" aSmalltalkClass selectors do: [:methodName | | method |

method := self makeFMethodNamed: methodNamehasClassScope: (isMetaClass & (haveExplicitMetaClasses not))onClassNamed: classDef name…

Page 30: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.30

After Some RefactoringsclassWithMethodsOnly: aSmalltalkClass

self reifyClassFor: aSmalltalkClass.self reifyInheritanceFor: aSmalltalkClass.self reifyAllMethodsOf: aSmalltalkClass

reifyClassFor: aSmalltalkClassclassDefName := self classNameFor: aSmalltalkClass

isMetaClass: self isMetaClass.classDef := self makeFClassNamed: classDefName.self inferClassPropertiesFor: classDef smalltalkClass: aSmalltalkClass isMetaClass: self isMetaClass

reifyMethodFor: aSmalltalkClass withName: methodName | method | method := self makeMethodNamed: methodName

hasClassScope: self isMetaClass & self haveExplicitMetaClasses not onClassNamed: classDef name.

self inferMethodPropertiesFor: method class: classDef smalltalkClass: aSmalltalkClass isMetaClass: self isMetaClass.

Page 31: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.31

Assessing code duplication with Duploc

Beforerefactoring

After refactoring

Page 32: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.32

Obstacles to RefactoringComplexity

• Changing design is hard• Understanding code is hard

Possibility to introduce errors• Run tests if possible• Build tests

Clean first Then add new functionality

Cultural Issues• Producing negative lines of code, what an idea!• “We pay you to add new features, not to improve the

code!”If it ain’t broke, don’t fix it

• “We do not have a problem, this is our software!“

Page 33: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.33

Obstacles to Refactoring• Performance

• Refactoring may slow down the execution• The secret to write fast software:

“Write tunable software first then tune it”Typically only 10% of your system consumes 90% of the resources so just focus on 10 %.

• Refactorings help to localize the part that need change

• Refactorings help to concentrate the optimizations

• Development is always under time pressure • Refactoring takes time• Refactoring better right after a software release

Page 34: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.34

Conclusion: Know-when & Know-how

• Know when is as important as know-how• Refactored designs are more complex• Use “code smells” as symptoms• Rule of the thumb: “Once and Only Once” (Kent

Beck)

Page 35: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.35

Further InformationMore about code smells and refactoring

• Book on refactoring [Fowl99a]http://www.refactoring.com

• Discussion site on code smellshttp://c2.com/cgi/wiki?CodeSmell

Page 36: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.36

Two Low-Level CuresLong methods

• A method is the smallest unit of overriding• Extract pieces as smaller method• Comments are good delimiters

Not Intention Revealing Methods • Rename Method

setType: aType "compute and store the variable type"self addTypeList: (ArrayType with: aType).currentType := (currentType computeTypes: (ArrayType with: aVal))

=> computeAndStoreType: aType

self addTypeList: (ArrayType with: aType).currentType := (currentType computeTypes: (ArrayType with: aType))

Page 37: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.37

One High-Level Cure: Duplicated Code

“Say everything exactly once” Kent Beck

Duplicated code makes the system harder to understand and to maintain

In the same class• Extract Method

Between two sibling subclasses• Extract Method • Push identical methods up to common superclass • Form Template Method

Between unrelated class• Create common superclass• Move to Component• Extract Component (e.g., Strategy)

Page 38: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.38

Other High-Level CuresGod Class

• Find logical sub-components (set of working methods/instance variables)

• Move methods and instance variables into components

Two possibilities• Extract Component => delegation

= “Extract Class” in [Fowl99a]• Extract Subclass

Page 39: 9.  Refactoring

© S. Demeyer, S. Ducasse, O. Nierstrasz Chapter.39

Nested ConditionalsNew cases should ideally not require changing existing code

May apply the State / Strategy / NullObject pattern

Use dynamic dispatch• Define subclasses• Define abstract method in superclass • Put every leg into an overriding subclass method