190 likes | 405 Views
Refactoring II. 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
E N D
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 • Design Patterns are always specified in UML • Model-View-Controller is a very common Design Pattern • An Antipattern is a common, but poor, 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
Example: Card cardId:int -copy:boolean=false «constructor» Card(int id) +isKind(desiredKind:int) +isSharable():boolean +toString():String Name of the class Variables [optional] Methods UML review I • Key: • + means public visibility • # means protected visibility • - means private visibility • <blank> means default (package) visibility • static variables are underlined
A B C D Factory Product A B creates 1..4 Class Ccontains 1 to 4 objectsof class D Other kinds ofrelations Class Bextendsclass A Class Bimplementsinterface A UML review II
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
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 * Command lhs:Command rhs:Commandvalue:int 5 x 0..2 Tree for 2 + 5 * x evaluate():int An example
class Add implements Command { int evaluate( ) { int v1 = lhs.evaluate().value; int v2 = rhs.evaluate().value; value = v1 + v2; return value; }} 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. Command lhs:Command rhs:Commandvalue:int 0..2 evaluate():int Using my “Command” pattern
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
Fish <<abstract>>move() BigFish LittleFish move() move() 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):
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 bothcheck if it’s ok to move there; // differentif 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
<<abstract>>Fish Fish move() <<abstract>>okToMove(locn):boolean <<abstract>>move() BigFish BigFish LittleFish BigFish okToMove(locn):boolean move() okToMove(locn):boolean move() The Fish refactoring • 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
The resultant code • In Fish: • public void move() {choose a random direction; // same for both find the location in that direction;// same for bothcheck if it’s ok to move there; // same for bothif 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}