Refactoring 2

Download Report

Transcript Refactoring 2

Refactoring II
26-Jul-16
Books I

Design Patterns is the classic book by “the Gang of Four”: Erich
Gamma, Richard Helm, Ralph Johnson, and John Vlissides




Basically a catalog of program organizations
Reading it is rather like reading a dictionary
Although equally applicable to Java, the examples are in C++
Head First Design Patterns by Elizabeth Freeman


Fun, irreverent, and entertaining
IMHO the best book for developing an understanding of what design
patterns are all about
Books II
AntiPatterns: Refactoring Software, Architectures, and Projects in
Crisis by William J. Brown, Raphael C. Malveau, Hays W.
"Skip" McCormick, Thomas J. Mowbray



Describes bad patterns and how to fix them
More fun to read than the Gang of Four book
Refactoring: Improving the Design of Existing Code by Martin
Fowler, with contributions from Kent Beck and others


Very clear, plenty of useful ideas, very readable
Much of this lecture will be based on this book
Design Patterns and Refactoring

Design Patterns describe good solutions to common (or
at least, not extremely rare) design problems



An Antipattern is a common, but poor, Design Pattern


Design Patterns are always specified in UML
Model-View-Controller is a very common Design Pattern
Antipatterns can be refactored into better designs
Refactoring is rearranging existing code while
maintaining the same functionality

Refactoring is usually done in terms of applying some Design
Pattern
UML review I
Name of the class
Variables [optional]
Methods

Key:





Example:
Card
cardId:int
-copy:boolean=false
«constructor» Card(int id)
+isKind(desiredKind:int)
+isSharable():boolean
+toString():String
+ means public visibility
# means protected visibility
- means private visibility
<blank> means default (package) visibility
static variables are underlined
UML review II
A
A
C
Factory
1..4
B
Class B
extends
class A
B
Class B
implements
interface A
D
Class C
contains
1 to 4 objects
of class D
creates
Product
Other kinds of
relations
Bad smells


Where did this term come from?
“If it stinks, change it.”
--Grandma Beck, discussing child-rearing philosophy

The basic idea is that there are things in code that cause
problems





Duplicated code
Long methods
Etc.
A common reason for refactoring is that you need to add
features that don’t fit well with the existing design
Any time you change working code, you run the risk of
breaking it

A good test suite makes refactoring much easier and safer
An example






Some time ago I was working
on code to evaluate
expressions
Expressions can be parsed
into a tree structure
Now what?
You could walk the tree and, at
each node, use a switch
statement to do the right thing
Adding a new operator requires
modifying existing code
I “discovered” a better solution
+
2
*
5
x
Tree for 2 + 5 * x
Command
lhs:Command
rhs:Command
0..2 value:int
evaluate():int
Using my “Command” pattern




class Add implements Command {
int evaluate( ) {
int v1 = lhs.evaluate().value;
int v2 = rhs.evaluate().value;
value = v1 + v2;
return value;
}
}
Command
lhs:Command
rhs:Command
0..2 value:int
evaluate():int
To evaluate the entire tree, evaluate the root node
To add an operator, add another class that extends Command
This is just a rough description; there are a lot of other details to consider



Some operands are unary
You have to look up the values of variables
Etc.
Duplicated code I

If the same code fragment occurs in more than one place
within a single class, you can use Extract Method




Turn the fragment into a method whose name explains the
purpose of the method
Any local variables that the method requires can be passed as
parameters (if there aren’t too many of them!)
If the method changes a local variable, see whether it makes
sense to return that as the value of the method (possibly
changing the name of the method to indicate this)
If the method changes two or more variables, you need other
refactorings to fix this problem
Duplicated code II

If the same code fragment occurs in sibling classes, you
can use Extract Method in both classes, then use Pull
Up Method








Use ExtractMethod in each class
Be sure the code is identical
If necessary, adjust the method signatures to be identical
Copy the extracted method to the common superclass
Delete one subclass method
Compile and test
Delete the other subclass method
Compile and test
Duplicated code III

If the same code fragment occurs in unrelated classes,
you can use Extract Method in one class, then:





Use this class as a component in the other class, or
Invoke the method from the other class, or
Move the method to a third class and refer to it from both of
the original classes
In any case, you need to decide where the method
makes most sense, and put it there
Eclipse has an Extract method... command that does
most of this work for you
Duplicated code IV


If almost the same code fragment occurs in sibling
classes, use Extract Method to separate the similar bits
from the different bits, and use Form Template Method
I’ll give an example in a moment
The Template Method

Template Methods lead to an inverted control structure




A superclass calls methods in its subclass
Template methods are so fundamental that they can be
found in almost every abstract class
Template Method uses inheritance
A similar pattern, Strategy Pattern, uses delegation
rather than inheritance
Big fish and little fish

The scenario: “big fish” and “little fish” move around in an
“ocean”




Fish move about randomly
A big fish can move to where a little fish is (and eat it)
A little fish will not move to where a big fish is
Here’s the initial code (before refactoring):
Fish
<<abstract>>move()
BigFish
LittleFish
move()
move()
The move() method

General outline of the method:


public void move() {
choose a random direction;
// same for both
find the location in that direction; // same for both
check if it’s ok to move there;
// different
if it’s ok, make the move;
// same for both
}
To do the Form Template Method:




Extract the check on whether it’s ok to move
In the Fish class, put the actual (template) move() method
Create an abstract okToMove() method in the Fish class
Implement okToMove() in each subclass
The Fish refactoring
Fish

<<abstract>>move()
BigFish
LittleFish
move()
move()

<<abstract>>Fish
move()
<<abstract>>okToMove(locn):boolean

Note how this works:
When a BigFish tries
to move, it uses the
move() method in
Fish
But the move()
method in Fish uses
the okToMove(locn)
method in BigFish
And similarly for
LittleFish
BigFish
BigFish
okToMove(locn):boolean
okToMove(locn):boolean
The resultant code

In Fish:



public void move() {
choose a random direction;
// same for both
find the location in that direction; // same for both
check if it’s ok to move there;
// same for both
if it’s ok, make the move;
// same for both
}
abstract boolean okToMove(locn);
In BigFish and in LittleFish:

boolean okToMove(locn) {
code for this kind of fish
}
To be continued...