Download presentation
Presentation is loading. Please wait.
1
SWEN-610 Foundations of Software Engineering
Refactoring SWEN-610 Foundations of Software Engineering
2
Lehmann & Belady: Laws of Software Evolution
Continuing Change - Systems must be continually adapted else they become progressively less satisfactory. Increasing Complexity - As a system evolves its complexity increases unless work is done to maintain or reduce it.
3
Refactoring is taking software which through natural processes has lost its original clean structure…
4
…and restoring a clean structure.
5
The definitive guide to refactoring is a book by Martin Fowler.
Refactoring: Improving the Design of Existing Code Martin Fowler, Addison-Wesley, 1999. See
6
Refactoring should only change internal structure and not observable behavior.
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
7
The design entropy of a software system tends to increase over time.
If you no longer can see the design, how can you stay consistent to it? Design Design Time
8
The entropy will increase because of the typical development death spiral.
Good design up front Local modifications alter the framework Short-term goals win out over structure maintenance Engineering sinks into hacking Integrity and structure fade (entropy)
9
A refactoring activity can remove some of that design randomness.
Fix or add a feature, and break one (or two)! Refactoring Design Entropy Refactor or Redo? Time
10
It is usually hard to counter, “If it ain’t broke, don’t fix it.”
Generally improves product quality Pay today to ease work tomorrow. May actually accelerate today’s work
11
Ward Cunningham’s Code Debt Metaphor
Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite [refactor – ed.]. Objects make the cost of this transaction tolerable. The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object-oriented or otherwise.
12
Refactoring does not work well as an end task because there never is any time to do it.
Refactoring should be a continuous code improvement activity: If it will make adding a new feature easier. If it will aid with debugging. If it fills a design hole. As a result of code inspection. If it simply makes the code easier to understand. Do the right thing now before doing the right thing will take too much time…and be too risky!
13
While we are on the subject of doing the
Right Thing …
14
Spreads design and implementation knowledge through team
Code inspections have been found to be the most effective technique for early defect detection. Spreads design and implementation knowledge through team Helps mentor less experienced developers New eyes see things “old” eyes are not seeing Did your team do any code inspections on your implementation? Next time when you can not find that bug, inspect don’t debug!
15
Some complain that all this patterns stuff makes the code run slower.
Refactored code may run slower Do you notice? Do you care? Ways to write fast code Strict time budgets hard real-time Constant attention optimize always (!?) Write for speed – any and all “parlor” tricks Obscures intentions Harder to upgrade code later Often does not help (80/20 rule) Performance profiling – the intelligent engineer’s guide. Make it work. Make it right. Make it fast.
16
If It Stinks, Change It.
17
There are many bad smells that get designed and coded into software.
Duplicated code Long methods Large classes Long parameter lists Orthogonal purposes for a class Shotgun changes Feature envy Data clumping Primitive object avoidance Switch statements Type codes Speculative generality Middle man overuse Inappropriate intimacy Data classes Verbose comments
18
What can we do with the type code?
If type does not affect behavior of object but type is shared Replace type code with class This allows type checking where data is shared If type effects behavior of object But never changes after instantiation Replace type code with subclasses Is modified after instantiation Replace type code with state or strategy, as appropriate if(type == TYPE_A) { code for TYPE_A … } else if(type == TYPE_B) { code for TYPE_B … } else if(type == TYPE_X) { code for TYPE_C … } else { code for unknown type … }
19
Replace temp with query Replace method with method object
Martin Fowler’s book is a cookbook for getting rid of smells using common refactoring operations. Extract method Inline method Replace temp with query Replace method with method object Substitute algorithm Extract class Hide delegate Remove middle man Replace type code with class Replace type code with state/strategy Replace type code with subclasses Introduce parameter object Replace inheritance with delegation Plus 70 others
20
Refactoring Steps
21
A catalog of refactorings from Fowler
We will look at Simplifying Conditional Expressions >> Replace Conditional with Polymorphism
22
Replace Conditional with Polymorphism
Example Refactoring Replace Conditional with Polymorphism You have a conditional that chooses different behavior depending on the type of an object. Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract.
23
Standard Example double getSpeed() { switch (_type) { case EUROPEAN:
return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts; case NORWEGIAN_BLUE: return (_isNailed) ? 0 : getBaseSpeed(_voltage); } throw new RuntimeException ("Should be unreachable");
24
Mechanics of applying Replace Conditional with Polymorphism
Compile and test Establish the necessary inheritance structure Extract the conditional part of a larger method into its own method (Extract Method/compile and test) Place the conditional at the top of the inheritance structure (Move Method/compile and test) Pick one of the subclasses and “move its leg” Repeat for each leg (compile and test) Make the superclass method abstract
25
Example: Employee Pay by Type
26
Example code class Employee... int payAmount() { switch (getType()) {
case EmployeeType.ENGINEER: return _monthlySalary; case EmployeeType.SALESMAN: return _monthlySalary + _commission; case EmployeeType.MANAGER: + _bonus; default: throw new RuntimeException("Incorrect Employee"); } int getType() { return _type.getTypeCode(); private EmployeeType _type; abstract class EmployeeType... abstract int getTypeCode(); class Engineer extends EmployeeType int getTypeCode() { return Employee.ENGINEER; } ... and other subclasses
27
Natural subclass structure Case statement is already nicely extracted
Observations Natural subclass structure Case statement is already nicely extracted So Move case statement to employee type That is the class that is being subclassed
28
Move case statement to employee type
Pass employee data as an argument class EmployeeType... int payAmount(Employee emp) { switch (getTypeCode()) { case ENGINEER: return emp.getMonthlySalary(); case SALESMAN: return emp.getMonthlySalary() + emp.getCommission(); case MANAGER: + emp.getBonus(); default: throw new RuntimeException( "Incorrect Employee"); } Compile
29
Change the payAmount method in Employee to delegate to the new class
Delegate to new class Change the payAmount method in Employee to delegate to the new class class Employee... int payAmount() { return _type.payAmount(this); }
30
Work on the case statement, one leg at a time
Copy the Engineer leg of the case statement onto the Engineer class This new method overrides the case statement for engineers. Be paranoid: put a trap class Engineer... int payAmount(Employee emp) { return emp.getMonthlySalary(); } class EmployeeType... int payAmount(Employee emp) { switch (getTypeCode()) { case ENGINEER: throw new RuntimeException ( "Should be being overridden"); case SALESMAN: return emp.getMonthlySalary() + emp.getCommission(); case MANAGER: + emp.getBonus(); default: throw new RuntimeException("Incorrect Employee"); }
31
Repeat for remaining legs
Compile and Test class Salesman... int payAmount(Employee emp) { return emp.getMonthlySalary() + emp.getCommission(); } class Manager... return emp.getMonthlySalary() + emp.getBonus(); Compile and Test Compile and Test
32
Declare the superclass method abstract
class EmployeeType... abstract int payAmount(Employee emp); Compile and Test
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.