320 likes | 408 Views
What is Refactoring?. CSE301 University of Sunderland Harry R. Erwin, PhD. Purpose. The purpose of this lecture is to introduce a way of cleaning up working code. This is called refactoring and is part of current industry practice .
E N D
What is Refactoring? CSE301 University of Sunderland Harry R. Erwin, PhD
Purpose • The purpose of this lecture is to introduce a way of cleaning up working code. • This is called refactoring and is part of current industry practice. • You are expected to experiment with it inyour PBL projects. • This lecture is organised to structure your investigation of refactoring. You will need to do some reading in the sources.
Sources • Fowler, 2000, Refactoring: Improving the Design of Existing Code, Addison-Wesley. Available electronically at the library. • http://www.amazon.com/exec/obidos/tg/detail/-/0321109295/103-4060125-0311065 • http://www.amazon.com/exec/obidos/ASIN/0130648841/103-4060125-0311065 • http://www.refactoring.com/catalog/ • http://www.win.ua.ac.be/~lore/refactoringProject/index.php • Extensive discussions on the comp.object newsgroup.
Topics • Test-Driven Development • JUnit • Refactoring • Principles • Bad Smells in Code • A Catalogue of Refactorings
Test-Driven Development • Refactoring depends on having good unit tests. This means you need to be doing test-driven development. • Test-driven development is a method of software development where tests specify interfaces of implementation and all code must have passed the tests. (Wikipedia)
How Bob Martin Describes TDD (personal communication) Erwin: TDD as I understand it: 1. Write a test for a bit of functionality. 2. Show that it fails. 3. Write the code to make the test pass. Martin: A good summary, but there's more. 1. We do not write production code until there is a failing test. 2. We write the simplest possible production code to get the test to pass. 3. We do not write more tests when we have a failing test. 4. We do not add to a failing test.
Martin Comments Further • If you watched someone doing TDD you would see them oscillating between test code and production code once every minute or so. • During each oscillation the programmer would add a few lines to his test code, thus making it fail (or not compile) and then add just a few lines to his production code in order to make the test pass (or compile). • Each oscillation is so simple that it's not worth taking. • Each oscillation is so simple that the risk of error is close to zero. • If you walk into a room of people working this way, and chose anyone at random, a minute ago all his code would have been working.
Refactoring • Deals with code rot. Your program works but it has become ugly as it has been modified. • “Refactoring is a technique to restructure code in a disciplined way. For a long time it was a piece of programmer lore, done with varying degrees of discipline by experienced developers, but not passed on in a coherent way.” (Fowler) • Will be very useful for your projects.
Refactoring Principles • Change only the implementations • Use it to improve the design of software • Use it to make software more understandable • Use it to help find bugs • Use it to help you program faster • Use it to prepare your current program for modification.
When? • “Three strikes and you refactor.” • The third time you have to cope with something ugly, fix it. particularly… • When you add functionality • When you need to fix a bug • When you do a code review
Problems with Refactoring • The Manager (expectation management) • Overuse (spinning your wheels) • Databases (database schemas are rigid!) • Public Interfaces (avoid changing things the user depends on) • A Design that Commits You (In ham and eggs, the hen is involved; the pig is committed. Don’t go there.)
How Software May “Smell”: • The system is rigid—it’s hard to change because everything has to change at once. • The system is fragile—changes cause the system to break in the strangest of places. • The system is immobile—that is, not reusable. • The system is viscous—doing things the right way is hard. • The system is needlessly complex. • The system contains mindless repetition. • The system is opaque—hard to understand. Have you ever seen software with these problems?
What “Bad Smells” Smell Like • Duplicated Code—cut and pasted everywhere • Long Method—hard to understand • Long Parameter List—multi-line calls • Parallel Inheritance—two hierarchies ‘joined at the hip’ • Divergent Change—violates the Single Responsibility Principle. • Shotgun Surgery—can’t change just one thing • Feature Envy—a class needs lots of methods from another class. • Data Clumps—data always used together • Large Class—everything including kitchen sink • Primitive Obsession—procedural coding style • Switch Statements—and duplicated cases
More “Bad Smells” (in Code) • Lazy Class—does no useful work • Speculative Generality—hooks that are never used • Temporary Field—variables only used sometimes • Message Chains—reference to a reference to a ref… • Middle Man—everything is delegated to a second class • Inappropriate Intimacy—’friends’ • Alternative Classes/Different Interfaces • Incomplete Library Class—lacks useful methods • Data Class—hold data, nothing else • Refused Bequest—inherited methods ignored • Comments—as deodorants
The Basic Rule of Refactoring • “Refactor the low hanging fruit” http://c2.com/cgi/wiki?RefactorLowHangingFruit • Low Hanging Fruit (def): “The thing that gets you most value for the least investment.” • In other words, don’t spend much time on it. There are always ways to improve any design incrementally. We will explore a few of them.
The Goal of Refactoring • To improve code without changing what it does. • This in some ways is similar to how an optimizing compiler restructures code. • Eclipse automates many of these actions. • Think about why they work!
A Catalog of Refactorings • Composing Methods • Moving Features Between Objects • Organizing Data • Simplifying Conditionals • Making Method Calls Simpler • Generalization • Big Refactorings
1. Composing Methods • Extract Method • Inline Method • Inline Temp • Replace Temp with Query • Introduce Explaining Variable • Split Temporary Variable • Remove Assignments to Parameters • Replace Method with Method Object • Substitute Algorithm
Comments • Extract Method is the most important, since it takes a clump of code and makes it into a small method. Eclipse handles this very well. It has problems with local variables, so some of the specialized methods handle those. I use this a lot. • Inline Method is the reverse method. Use it if Extract Method creates ugly code. • Replace Method with Method Object moves local variables of a method into the class fields of a ‘functor’ class, so Extract Method can be used more easily.
2. Moving Features Between Objects • Move Method • Move Field • Extract Class • Inline Class • Hide Delegate • Remove Middle Man • Introduce Foreign Method • Introduce Local Extension
Comments • Remember CRC cards? • You never get responsibilities right the first time. This is how you change your mind. • If you need to move functions between objects, these refactorings handle it. Move Method, Move Field, Extract Class, and Inline Class are the heavyweights here. • The remainder are special purpose methods to support the Big Four.
Self Encapsulate Field Replace Data Value with Object Change Value to Ref Change Ref to Value Replace Array with Object Duplicate Observed Data Switch Uni/Bidirectional Association Replace Magic Number Encapsulate Field Encapsulate Collection Replace Record Replace Type Code with Class/Subclass/ State/Strategy Replace Subclass with Fields 3. Organizing Data
Comments • These generally clean up problems with how classes define and access fields. I use encapsulate field a lot. • A common need in student projects. • Value objects are immutable objects that are equal if their fields are equal—you have to override equals() and hashCode() for them. • The most advanced refactoring here is probably Duplicate Observed Data. That’s how you fix a design that mixes model or business logic with GUI or SQL code.
4. Simplifying Conditionals • Decompose it • Consolidate it • Consolidate Duplicate Fragments • Remove Control Flag • Replace Nested Conditional with Guard Clauses • Replace with Polymorphism • Introduce Null Object • Introduce Assertion
Comments • Involve creation of polymorphic classes. Remember this when you learn about polymorphism next term. • These mostly simplify conditional logic (if/then/else). Decompose Conditional is the most important. The parts of an if/then/else are replaced with method calls. These also clean up the use of flags to control sequencing or returns: • if(o.foo()) o.bar() else o.baz(); • Switch statements can also be replaced with polymorphic classes. • A null object is a real ‘do-nothing’ object that substitutes for a null value in a reference. It obviates the need to do an instanceOf test to check for null.
Rename Method Add Parameter Remove Parameter Separate Query from Modifier Parameterize Method Replace Parameter with Explicit Methods Preserve Whole Object Replace Parameter with Method Introduce Parameter Object Remove Setting Method Hide Method Replace Constructor with Factory Method Encapsulate Downcast Replace Error Code with Exception Replace Exception with Test 5. Making Method Calls Simpler
Comments • These refactorings make interfaces easier to understand. • Rename Method is convenient to document what a method does. Peter Dunne likes it a lot. • Most of the remaining refactorings are used to get rid of parameters, but be cautious in concurrent programming. (Ask Chris Knowles why.) • Factory methods hide the concrete implementation of an interface. Remember this when you learn about the Dependency Inversion Principle next term. This is the answer.
Pull Up Field Pull Up Method Pull Up Constructor Body Push Down Method Push Down Field Extract Subclass Extract Superclass Extract Interface Collapse Hierarchy Form Template Method Replace Inheritance with Delegation Replace Delegation with Inheritance 6. Generalization
Comments • I find it hard to get inheritance right from the beginning. • These refactorings help clean up messy inheritance hierarchies (like those in your projects). • They also allow you to evolve inheritance hierarchies ‘on the fly’, another common need. • Sometimes delegation works better than inheritance or vice versa. Try both. (I will talk about ‘delegate, delegate, delegate’ again.)
7. Big Refactorings • Tease Apart Inheritance • Create two hierarchies from a complex one and use delegation to invoke one from the other. • Convert Procedural Design to Objects • Turn data into objects; break up the behaviour; finally move the behaviour into the objects. • Separate Domain from Presentation • Move domain logic into separate domain classes. • Extract Hierarchy • Subclass a very complex class with many special cases. (A common need in student projects)
Comments • Use “Tease Apart Inheritance” to simulate multiple inheritance. • Use “Convert Procedural Design to Objects” to convert a procedural program to OO. • Use “Separate Domain from Presentation” to fix a messy GUI. • Use “Extract Hierarchy” to clean up complexity, particularly involving many special cases.
Conclusions • Kent Beck says: This is “only the beginning.” • Why? Questions we’ve left unaddressed include when to use refactoring and when to let well enough alone. That will be another lecture. • Beck’s advice: • Pick an achievable goal • Stop when you’re unsure • Backtrack if necessary • Work with a partner • I have some exercises involving refactoring.