Download presentation
Presentation is loading. Please wait.
Published byDiego Farwell Modified over 9 years ago
1
Refactoring This lecture is divided into and introduction to refactoring and then several lessons. The intent is not to teach you all the refactorings but to expose you to just a few…
2
Refactoring Introduction Outline A. What is Refactoring? B. Why do we Refactor? C. How do we Refactor? D. When do we Refactor?
3
Refactoring A. What is Refactoring? Refactoring is a technique which identifies bad code (code that smells) and allows promotes the re-structuring of that bad code into classes and methods that are more readable, maintainable, and generally sparse in code. Refactoring yields a “better” design of both your classes and methods.
4
Refactoring B. Why do we Refactor? Refactoring allows building of complex systems by exploiting sophisticated object- oriented techniques that yield such systems catagorized as frameworks, beans, or other reusable software components.
5
Refactoring Why do you refactor Enable sharing of logic. Explain intention and implementation seperately. Isolate change. Encode conditional logic.
6
Refactoring C. How do we Refactor? Refactoring is accomplished by 1)building, decomposing, and moving methods 2)building and decomposing classes 3)replacing code with patterns, and 4)replacing code with other techniques.
7
Refactoring D. When do we Refactor? Refactoring is done when 1)methods or classes are too LARGE, 2)code, control flow, or data structures are DUPLICATED, 3)attributes or methods are MISPLACED, 4)When parameters can make code reusable 5)When design is poor (the code smells).
8
Refactoring When do you refactor Refactor when you add functions. Refactor as you do a code review. Refactor when you fix a bug.
9
Refactoring Benefits of Refactoring Without refactoring, the design of the program decays. Refactoring helps you to develop code more quickly. Refactoring helps you to find bugs. Refactoring makes software easier to understand. Refactoring improves the design of software.
10
Refactoring An example- Class Diagram Movie getPriceCode)_ setPriceCode() getTitle() pricecode: int children = 2 regular = 0 new_release=1 title: String Rental getMovie() getDaysRented)_ daysRented:int Customer Statement() addRental(rental) getName(): name: String rentals: vector 0..* 1..10..* 1..1
11
Refactoring MovieRentalCustomer statement forallrentals Rental: getMovie() getPriceCode() getDaysRented() An Example – Sequence Diagram
12
Refactoring public class Movie { public static final int CHILDRENS = 2; // type movie for children public static final int REGULAR = 0; // type of regular movie public static final int NEW_RELEASE = 1; // new release movie private String _title; // title of the movie private int _priceCode; // price code for movie public Movie(String title, int priceCode) { _title = title; _priceCode = price Code; }// end constructor public int getPriceCode() { return priceCode; } public void setPriceCode (int agr) { _priceCode = arg; } public String getTitle () { return _Title; } } // end Movie Code -- page 3 Constructor Movie getPriceCode)_ setPriceCode() getTitle() pricecode: int children = 2 regular = 0 new_release=1 title: String
13
Refactoring class Rental { private Movie _movie; // movie rented private int _daysRented; // number of days rental public Rental(Movie movie, int daysRented) { _movie = movie; _daysREnted = daysRented; } // end Rental public int getDaysRented() { return _daysRented; } public Movie getMovie() { return _movie; } }// end Rental Code (con’t)-- page 3 Constructor Rental getMovie() getDaysRented)_ daysRented:int
14
Refactoring class Customer { private String _name; // name of customer private Vector _rentals = new Vector (); // vector of list of rentals by the customer public Customer (String name) { _name = name; } public void addRental (Rental arg) { _rentals.addElement(arg) } public String getName () { return _name} Code (con’t)-- page 4 Constructor Customer Statement() addRental(rental) getName(): name: String rentals: vector
15
Refactoring public String statement() { double totalAmount = 0; // total of the statement int frequentRenterPoints = 0; // number of frequent rental points of a rental Enumeration rentals = _rentals.elements(); // list of rentals String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { double thisAmount =0; Rental each = (Rental) rentals.nextElement(); // continued on next page Code (con’t)-- page 5
16
Refactoring // determine amounts for each line // regular 2.00 for two days 1.50 extra // new release 3.00 per day // children 1.50 for three days switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: // statements for a regular movie thisAmount +=2; if (each.getDaysRented() >2) thisAmount +=(each.getDaysRented()-2)*1.5; Break; case Movie.NEW_RELEASE: // statements for a new release type movie thisAmount +=each.getDaysRented()*3; Break; case Movie_CHILDREN: // statements for a children movie thisAmount +=1.5; if (each.getDaysRented() >3) thisAmount +=(each.getDaysRented()- 3)*1.5; Break; } // end switch Code (Con’t) page 5
17
Refactoring // add frequent renter points add 1 frequent renter point if NEW RELEASE rented > one day frequentRenterPoints ++; // add bonus for a two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints ++; // show figures for this rental result +=“/t” + each.getMovie().getTitle*(+”\t” + String.valueOf(thisAmount) + “\n”; totalAmount +=thisAmount; } // add footer lines result +=“Amount owed is “ + String.valueOf(totalAmount) + “\n”; result += “You earned “ + String.valueOf(frequentRenterPoints) + “ frequent renter points”; return result; } // end statement // end customer Code (Con’t) page 5
18
Refactoring This method, called statement, is TOO LARGE. This statement method should not be inside customer.
19
Refactoring Refactoring Opportunities // determine amounts for each line switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount +=2; if (each.getDaysRented() >2) thisAmount +=(each.getDaysRented()- 2)*1.5; Break; case Movie.NEW_RELEASE: thisAmount +=each.getDaysRented()*3; Break; case Movie_CHILDREN: thisAmount +=1.5; if (each.getDaysRented() >3) thisAmount +=(each.getDaysRented()- 3)*1.5; Break; } // end switch a code cluster is setting one variable EXTRACT it as a METHOD returning the variable
20
Refactoring EXTRACT METHOD page 11 private double amountFor(Rental each) double thisAmount = 0.0; switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: thisAmount +=2; if (each.getDaysRented() >2) thisAmount +=(each.getDaysRented()-2)*1.5; Break; case Movie.NEW_RELEASE: thisAmount +=each.getDaysRented()*3; Break; case Movie_CHILDREN: thisAmount +=1.5; if (each.getDaysRented() >3) thisAmount +=(each.getDaysRented()-3)*1.5; Break; } // end switch return thisAmount; } // end amountFor
21
Refactoring Original Code - Customer Class page 18 Calls the new method public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { double thisAmount =0; Rental each = (Rental) rentals.nextElement(); thisAmount = amountFor(each);
22
Refactoring Refactoring Opportunities Should NOT be in customer class Should be in the rental public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { double thisAmount =0; Rental each = (Rental) rentals.nextElement(); thisAmount = amountFor(each); a variable resides in the wrong class MOVE the METHOD to the class where it should reside
23
Refactoring MOVE METHOD and rename private double getCharge(Rental each) double result = 0.0; switch (each.getMovie().getPriceCode()) { case Movie.REGULAR: result +=2; if (each.getDaysRented() >2) result +=(each.getDaysRented()-2)*1.5; Break; case Movie.NEW_RELEASE: result +=each.getDaysRented()*3; Break; case Movie_CHILDREN: result +=1.5; if (each.getDaysRented() >3) result +=(each.getDaysRented()-3)*1.5; Break; } // end switch return result; } // end getCharge Rename the method and result
24
Refactoring Original Code - Customer Class page 19 Calls the new method In the rental class public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { double thisAmount =0; Rental each = (Rental) rentals.nextElement(); thisAmount = each.getCharge(each);
25
Refactoring MovieRentalCustomer statement forallrentals getPriceCode() getDaysRented() An Example – Sequence Diagram Rental: getMovie()amount: getCharge()
26
Refactoring // add frequent renter points add 1 frequent renter point if NEW RELEASE rented > one day frequentRenterPoints ++; // add bonus for a two day new release rental if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) frequentRenterPoints ++; // show figures for this rental result +=“/t” + each.getMovie().getTitle*(+”\t” + String.valueOf(thisAmount) + “\n”; totalAmount +=thisAmount; } // add footer lines result +=“Amount owed is “ + String.valueOf(totalAmount) + “\n”; result += “You earned “ + String.valueOf(frequentRenterPoints) + “ frequent renter points”; return result; } // end statement Refactoring Opportunities a code cluster is setting one variable EXTRACT it as a METHOD returning the variable }
27
Refactoring // add frequent renter points add 1 frequent renter point if NEW RELEASE rented > one day int getFrequentRenterPoints() { if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) return 2; else return 1; EXTRACT METHOD
28
Refactoring Refactoring Opportunities Should NOT be in customer class Should be in the rental public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { double thisAmount =0; Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += getFrequentRenterPoints(); a variable resides in the wrong class MOVE the METHOD to the class where it should reside
29
Refactoring class Rental….. ….. // add frequent renter points add 1 frequent renter point if NEW RELEASE rented > one day int getFrequentRenterPoints() { if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDaysRented() > 1) return 2; else return 1; MOVE METHOD
30
Refactoring Original Code - Customer Class Calls the new methods In the rental class public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { double thisAmount =0; Rental each = (Rental) rentals.nextElement(); thisAmount = each.getCharge(); frequentRenterPoints += each.getFrequentRenterPoints();
31
Refactoring public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { double thisAmount =0; Rental each = (Rental) rentals.nextElement(); thisAmount = each.getCharge(); frequentRenterPoints += getFrequentRenterPoints(); // show figures for this rental result +=“/t” + each.getMovie().getTitle*(+”\t” + String.valueOf(thisAmount) + “\n”; totalAmount +=this.Amount; } // end while Refactoring Opportunities a variable is used temporarily REPLACE TEMP with QUERY eliminating the temp
32
Refactoring REPLACE TEMP with QUERY page 21 public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { double thisAmount =0; Rental each = (Rental) rentals.nextElement(); thisAmount = each.getCharge(); frequentRenterPoints += getFrequentRenterPoints(); // show figures for this rental result +=“/t” + each.getMovie().getTitle*(+”\t” + String.valueOf(thisAmount each.getCharge()) + “\n”; totalAmount += this.Amount each.getCharge() ; } // end while
33
Refactoring public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += getFrequentRenterPoints(); // show figures for this rental result +=“/t” + each.getMovie().getTitle*(+”\t” + String.valueOf(each.getCharge()) + “\n”; totalAmount += each.getCharge(); } // end loop // add footer lines result +=“Amount owed is “ + String.valueOf(totalAmount) + “\n”; result += “You earned “ + String.valueOf(frequentRenterPoints) + “ frequent renter points”; return result; } // end statement Refactoring Opportunities a variable is used temporarily REPLACE TEMP with QUERY eliminating the temp Problem – temp in loop
34
Refactoring private double getTotalCharge() { double result = 0; Enumeration rentals = _rentals.elements(); while (rentals.hasMoreElements() { Rental each = (Rental) rentals.nextElement(); result += each.getCharge(); } // end loop return result; } // end getTotalCharge EXTRACT it as a METHOD
35
Refactoring public String statement() { double totalAmount = 0; int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += getFrequentRenterPoints(); // show figures for this rental result +=“/t” + each.getMovie().getTitle*(+”\t” + String.valueOf(each.getCharge()) + “\n”; totalAmount += each.getCharge(); } // add footer lines result +=“Amount owed is “ + String.valueOf( totalAmount getTotalCharge()) + “\n”; result += “You earned “ + String.valueOf(frequentRenterPoints) + “ frequent renter points”; return result; } // end statement REPLACE TEMP with QUERY page 27 Yes, we are looping twice
36
Refactoring public String statement() { int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += getFrequentRenterPoints(); // show figures for this rental result +=“/t” + each.getMovie().getTitle*(+”\t” + String.valueOf(each.getCharge()) + “\n”; } // end loop // add footer lines result +=“Amount owed is “ + String.valueOf(getTotalCharge) + “\n”; result += “You earned “ + String.valueOf(frequentRenterPoints) + “ frequent renter points”; return result; } // end statement Refactoring Opportunities a variable is used temporarily REPLACE TEMP with QUERY eliminating the temp Problem – temp in loop
37
Refactoring private double getTotalFrequentRentalPoints() { int result = 0; Enumeration rentals = _rentals.elements(); while (rentals.hasMoreElements() { Rental each = (Rental) rentals.nextElement(); result += each.getFrequentRentalPoints(); } // end loop return result; } // end getTotalFrequentRentalPoints EXTRACT it as a METHOD
38
Refactoring public String statement() { int frequentRenterPoints = 0; Enumeration rentals = _rentals.elements(); String result = “Rental Record for “ + getName() +”/n” while (rentals.hasMoreElements() { Rental each = (Rental) rentals.nextElement(); frequentRenterPoints += getFrequentRenterPoints(); // show figures for this rental result +=“/t” + each.getMovie().getTitle*(+”\t” + String.valueOf(each.getCharge()) + “\n”; } // add footer lines result +=“Amount owed is “ + String.valueOf( getTotalCharge()) + “\n”; result += “You earned “ + String.valueOf(frequentRenterPoints getTotalFrequentRentalPoints ) + “ frequent renter points”; return result; } // end statement REPLACE TEMP with QUERY page 29 Yes, we are looping thrice
39
Refactoring MovieRentalCustomer statement getTotalCharge amount: getCharge() getPriceCode() getFrequentRenterPoints() An Example – Sequence Diagram getPriceCode()
40
Refactoring Refactoring Opportunities conditional code exist for sub-types Use POLYMORPHISM replacing conditional logic private double getCharge(Rental each) double result = 0.0; switch (getMovie().getPriceCode()) { case Movie.REGULAR: result +=2; if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5; Break; case Movie.NEW_RELEASE: result +=getDaysRented()*3; Break; case Movie_CHILDREN: result +=1.5; if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5; Break; } // end switch return result; } // end getCharge Cannot make subclasses of movie Movies can change classifications
41
Refactoring Refactoring Opportunities object can change states in its lifetime Use the STATE Pattern for the variables which change values private double getCharge(Rental each) double result = 0.0; switch (getMovie().getPriceCode()) { case Movie.REGULAR: result +=2; if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5; Break; case Movie.NEW_RELEASE: result +=getDaysRented()*3; Break; case Movie_CHILDREN: result +=1.5; if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5; Break; } // end switch return result; } // end getCharge Cannot make subclasses of movie Movies can change classifications
42
Refactoring Original- Class Diagram Movie getPriceCode)_ setPriceCode() getTitle() pricecode: int children = 2 regular = 0 new_release=1 title: String Rental getMovie() getDaysRented)_ daysRented:int Customer Statement() addRental(rental) getName(): name: String rentals: vector 0..* 1..10..* 1..1
43
Refactoring State Pattern- Class Diagram Movie getPriceCode)_ setPriceCode() getTitle() getCharge() pricecode: int children = 2 regular = 0 new_release=1 title: String Price getCharge() 1..1 Regular Price getCharge() Childrens Price getCharge() New Release Price getCharge()
44
Refactoring Refactoring Opportunities state variables not encapsulated SELF ENCAPSULATE FIELDS add get and set methods private double getCharge(int daysRented) double result = 0.0; switch (getMovie().getPriceCode()) { case Movie.REGULAR: result +=2; if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5; Break; case Movie.NEW_RELEASE: result +=getDaysRented()*3; Break; case Movie_CHILDREN: result +=1.5; if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5; Break; } // end switch return result; } // end getCharge Cannot make subclasses of movie Movies can change classifications
45
Refactoring public class Movie { public static final int CHILDRENS = 2; public static final int REGULAR = 0; public static final int NEW_RELEASE = 1; private String _title; private int _priceCode; public Movie(String title, int priceCode) { _title = title; _priceCode = price Code; }// end constructor public int getPriceCode() { return priceCode; } public void setPriceCode (int agr) { _priceCode = arg; } public String getTitle () { return _Title; } } // end Movie Original Code -- page 3 } Sub-types Self Encapsulate
46
Refactoring public class Movie { public static final int CHILDRENS = 2; public static final int REGULAR = 0; public static final int NEW_RELEASE = 1; private String _title; private int _priceCode; setPriceCode(priceCode); public Movie(String title, int priceCode) { _title = title; _priceCode = price Code; }// end constructor public int getPriceCode() { return priceCode; } public void setPriceCode (int agr) { _priceCode = arg; } public String getTitle () { return _Title; } } // end Movie Self Encapsulating Movie Code -- page 41
47
Refactoring class Price { abstract int getPriceCode(); } // end Price class ChildrensPrice extends Price { int getPriceCode () { return Movie.CHILDRENS; } } // end ChildrensPrice class NewReleasePrice extends Price { int getPriceCode () { return Movie.NEW_RELEASE; } } // end NewReleasePrice class RegularPrice extends Price { int getPriceCode () { return Movie.REGULAR; } } // end RegularPrice Movie Price Sub-Classes -- page 41
48
Refactoring MOVE METHOD page 45 class Price private double getCharge(int daysRented) double result = 0.0; switch (getMovie().getPriceCode()) { case Movie.REGULAR: result +=2; if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5; Break; case Movie.NEW_RELEASE: result +=getDaysRented()*3; Break; case Movie_CHILDREN: result +=1.5; if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5; Break; } // end switch return result; } // end getCharge }// end price Once it is moved, we can replace conditional with polymorphism
49
Refactoring Use POLYMORPHISM page 47 class RegularPrice double getCharge(int daysRented) double result = 2; if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5; return result; } // end getCharge } // end regularPrice class ChildrensPrice double getCharge (int daysRented) { double result = 1.5; if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5; return result } // end getCharge } // end ChildrensPrice class NewRelease double getCharge (int daysRented() { return daysRented * 3; } Take one leg of case statement at a time.
50
Refactoring Use POLYMORPHISM page 47 class Price abstract double getCharge (int daysRented); Create an overiding method for the getCharge method.
51
Refactoring Use POLYMORPHISM page 48 class Movie….. int getFrequentRenterPoints (int daysRented) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented >1) return 2; else return 1; } We can do the same thing with getFrequentRenterPoints
52
Refactoring Final Thoughts page 52 Placing a state pattern in code is quite an effort The gain is that if I change any of the price’s behavior add new prices add extra price dependent behavior The rest of the application does not know about the use of the state pattern. For this tiny bit of behavior, it does not seem worthwhile. These changes lead to better distributed responsibilities and code 1that is easier to maintain. It does look different than the regular procedural code. One important lesson in this example is the rhythm of refactgoring, change a little test a little
53
Refactoring We will look at a catalogue of refactoring methods. Each refactoring method is described by the following: name: noun (with a page number of the text) summary: narrative description motivation: why you would use the technique example: code using the technique mechanics: how you would use the technique Catalogue of Refactoring
54
Refactoring We will look at several types of refactoring. These include the refactoring of: methodsgeneralization classesdata callsconditional expressions And some other BIG refactoring.
55
Refactoring Extract Methods from code Inline Methods to code Replace Temp with Query Introduce Explaining Variables Split Temporary Variables Remove Assignments to Parameters Replace Method with Method Objects Substitute Algorithm Refactoring and Composing Methods
56
Refactoring Move Method Move Field Extract Class Inline Class Hide Delegate Remove Middle Man Introduce Foreign Method Introduce Local Extension Refactoring by Moving Features Between Objects
57
Refactoring Self Encapsulate FieldEncapsulate Field Replace Data Value with ObjectEncapsulate Collection Change Value to ReferenceReplace Record with Data Class Change Reference to ValueReplace Type with Data Class Replace Array with ObjectReplace Type Code with Subclasss Duplicate Observed DataReplace Type Code with State/Strategy Change Unidirectional Direction to BidirectionalReplace Subclass Change Bidirectional Direction to Unidirectionalwith Field Replace Magic Number with Symbolic Constant Refactoring by Organizing Data
58
Refactoring Decompose Conditional Consolidate Conditional Expression Consolidate Duplicate Conditional Fragments Remove Control Flag Replace nested Conditional with Guard Clauses Replace Conditional with Polymorphism Introduce Null Object Introduce Assertion Refactoring by Simplifying Conditional Expressions
59
Refactoring Rename MethodIntroduce Parameter Object Add ParameterRemove Setting Method Remove ParameterHide Method Separate Query from ModifierReplace Constructor with Factory Parameterize MethodEncapsulate Downcast Replace Parameter with Explicit Methods Preserve Whole ObjectReplace Error Code with Exception Replace Parameter with MethodReplace Exception with Test Refactoring by Making Method Calls Simpler
60
Refactoring Pull Up FieldExtract Interface Pull Up MethodCollapse Hierarchy Pull Up Constructor BodyForm Template Method Push Down MethodReplace Inheritance with Push Down FieldDelegation Extract SubclassReplace Delegation with Extract SuperclassInheritance Refactoring by Dealing with Generalization
61
Refactoring Tease Apart Inheritance Convert Procedural Design to Objects Separate Domain from Presentation Extract Hierarchy Refactoring with Big Refactoring
62
Refactoring It if difficult to know how to approach refactoring the code you have written. And unfortunately, we have not progressed in teaching programming to notice needed refactorings. You can approach it using a few simple guidelines. Because there are SOOOO many factorings
63
Refactoring First: Make your code Self-Documenting Second: Encapsulate your classes Third: Assure Constants and Variables are coded correctly Fourth: Make sure you have GOOD Methods Fifth: Make sure your Conditionals are coded correctly Sixth: Assure your Classes are coded correctly Seventh: Assure proper Inheritance Eighth: Apply needed Patterns Refactoring Topics
64
Refactoring import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.net.*; import java.applet.*; /** * A Simple TicTacToe applet. * A Tic Tac Toe applet. * A very simple, and mostly brain-dead * implementation of your favorite game! */
65
Refactoring /** * * In this game a position is represented by a white and black * bitmask. A bit is set if a position is occupied. There are * 9 squares so there are 1<<9 possible positions for each * side. An array of 1<<9 Booleans is created, it marks * all the winning positions. * * @version 1.2, 13 Oct 1995 * @author Arthur van Hoff * @modified 96/04/23 Jim Hagen : winning sounds * @modified 03/07/21 Sara Stoecklin : updated java version */
66
Refactoring public class TTTV0 extends Applet implements MouseListener { /** * White's current position. The computer is white. */ int white; /** * Black's current position. The user is black. */ int black;
67
Refactoring /** * The squares in order of importance... */ final static int moves[] = {4, 0, 2, 6, 8, 1, 3, 5, 7}; /** * The winning positions. */ static boolean won[] = new boolean[1 << 9]; static final int DONE = (1 << 9) - 1; static final int OK = 0; static final int WIN = 1; static final int LOSE = 2; static final int STALEMATE = 3;
68
Refactoring /** * Mark all positions with these bits set as winning. */ static void isWon(int pos) { for (int i = 0 ; i < DONE ; i++) { if ((i & pos) == pos) { won[i] = true; }
69
Refactoring /** * Initialize all winning positions. */ static { isWon((1 << 0) | (1 << 1) | (1 << 2)); isWon((1 << 3) | (1 << 4) | (1 << 5)); isWon((1 << 6) | (1 << 7) | (1 << 8)); isWon((1 << 0) | (1 << 3) | (1 << 6)); isWon((1 << 1) | (1 << 4) | (1 << 7)); isWon((1 << 2) | (1 << 5) | (1 << 8)); isWon((1 << 0) | (1 << 4) | (1 << 8)); isWon((1 << 2) | (1 << 4) | (1 << 6)); }
70
Refactoring /** * Compute the best move for white. * @return the square to take */ int bestMove(int white, int black) { int bestmove = -1;
71
Refactoring loop: for (int i = 0 ; i < 9 ; i++) { int mw = moves[i]; if (((white & (1 << mw)) == 0) && ((black & (1 << mw)) == 0)) { int pw = white | (1 << mw); if (won[pw]) { // white wins, take it! return mw; }
72
Refactoring loop: ……. for (int mb = 0 ; mb < 9 ; mb++) { if (((pw & (1 << mb)) == 0) && ((black & (1 << mb)) == 0)) { int pb = black | (1 << mb); if (won[pb]) { // black wins, take another continue loop; }
73
Refactoring loop: …… …….. // Neither white nor black can win in one move, this will do. if (bestmove == -1) { bestmove = mw; } if (bestmove != -1) { return bestmove; }
74
Refactoring loop: …… // No move is totally satisfactory, try the first one that is open for (int i = 0 ; i < 9 ; i++) { int mw = moves[i]; if (((white & (1 << mw)) == 0) && ((black & (1 << mw)) == 0)) { return mw; } // No more moves return -1; }
75
Refactoring /** * User move. * @return true if legal */ boolean yourMove(int m) { if ((m 8)) { return false; } if (((black | white) & (1 << m)) != 0) { return false; } black |= 1 << m; return true; }
76
Refactoring /** * Computer move. * @return true if legal */ boolean myMove() { if ((black | white) == DONE) { return false; } int best = bestMove(white, black); white |= 1 << best; return true; }
77
Refactoring /** * Figure what the status of the game is. */ int status() { if (won[white]) { return WIN; } if (won[black]) { return LOSE; } if ((black | white) == DONE) { return STALEMATE; } return OK; }
78
Refactoring /** * Who goes first in the next game? */ boolean first = true; /** * The image for white. */ Image notImage; /** * The image for black. */ Image crossImage;
79
Refactoring /** * Initialize the applet. Resize and load images. */ public void init() { notImage = getImage(getCodeBase(), "oimage.gif"); crossImage = getImage(getCodeBase(), "ximage.gif"); addMouseListener(this); } public void destroy() { removeMouseListener(this); }
80
Refactoring /** * Paint it. */ public void paint(Graphics g) { Dimension d = getSize(); g.setColor(Color.black); int xoff = d.width / 3; int yoff = d.height / 3; g.drawLine(xoff, 0, xoff, d.height); g.drawLine(2*xoff, 0, 2*xoff, d.height); g.drawLine(0, yoff, d.width, yoff); g.drawLine(0, 2*yoff, d.width, 2*yoff);
81
Refactoring int i = 0; for (int r = 0 ; r < 3 ; r++) { for (int c = 0 ; c < 3 ; c++, i++) { if ((white & (1 << i)) != 0) { g.drawImage(notImage, c*xoff + 1, r*yoff + 1, this); } else if ((black & (1 << i)) != 0) { g.drawImage(crossImage, c*xoff + 1, r*yoff + 1, this); }
82
Refactoring /** * The user has clicked in the applet. Figure out where * and see if a legal move is possible. If it is a legal * move, respond with a legal move (if possible). */ public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY();
83
Refactoring switch (status()) { case WIN: case LOSE: case STALEMATE: play(getCodeBase(), "audio/return.au"); white = black = 0; if (first) { white |= 1 << (int)(Math.random() * 9); } first = !first; repaint(); return; }
84
Refactoring // Figure out the row/column Dimension d = getSize(); int c = (x * 3) / d.width; int r = (y * 3) / d.height; if (yourMove(c + r * 3)) { repaint();
85
Refactoring switch (status()) { case WIN: play(getCodeBase(), "audio/yahoo1.au"); break; case LOSE: play(getCodeBase(), "audio/yahoo2.au"); break; case STALEMATE: break;
86
Refactoring default: if (myMove()) { repaint(); switch (status()) { case WIN: play(getCodeBase(), "audio/yahoo1.au"); break; case LOSE: play(getCodeBase(), "audio/yahoo2.au"); break; case STALEMATE: break; default: play(getCodeBase(), "audio/ding.au"); }
87
Refactoring } else { play(getCodeBase(), "audio/beep.au"); } } else { play(getCodeBase(), "audio/beep.au"); }
88
Refactoring public void mousePressed(MouseEvent e) { } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public String getAppletInfo() { return "TicTacToe by Arthur van Hoff"; }
89
Refactoring Lesson One: Self-Documenting Code and Functional Testing
90
Refactoring Process 1. Read, review and understand 2. Format and comment 3. Perform documenting refactorings
91
Refactoring 1. Read, review and understand A. Read existing code Not part of refactoring but necessary. B. Review it for understandability Do the variables have meaningful names? Are their enough comments Do the methods have meaningful names?
92
Refactoring 2. Format and comment A. Format the code according to supplied standards Format the code by some standard to aid in readability Comment code for understanding After reading the code add any needed comments to increase understandability
93
Refactoring import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.net.*; import java.applet.*; /** * A Simple TicTacToe applet. A Tic Tac Toe applet. * A very simple, and mostly brain-dead * implementation of your favorite game! */ My format- get rid of extra white space to show on screens
94
Refactoring /** * A bitmask is used for the two players denoting positions occupied. * Each bit represents a square on the board 0..8. * The bit is 0 if not occupied and 1 if occupied. * Winning is determined by comparing the bitmask with * an array of Booleans that marks all the winning positions. * @version 1.2, 13 Oct 1995 * @author Arthur van Hoff * @modified 96/04/23 Jim Hagen : winning sounds * @modified 03/07/21 Sara Stoecklin : updated java version */ Rewrite comments to make them clearer
95
Refactoring public class TTTV11 extends Applet implements MouseListener { static final int DONE = (1 << 9) - 1; // sentinal for square loop static final int LOSE = 2; // status for user wins static final int OK = 0; // status for game continues static final int STALEMATE = 3; // status for a tie static final int WIN = 1; // status for computer wins Place all class constants first in alphabetical order
96
Refactoring int black; // user bitmask denotes user squares occupied Image crossImage; // user image boolean first = true; // who goes first next game // The squares in order of importance... positions 0..8 final static int moves[] = {4, 0, 2, 6, 8, 1, 3, 5, 7}; Image notImage; // computer image int white; // computer bitmask denotes squares occupied static boolean won[] = new boolean[1 << 9]; // winning states Place all class variables next in alphabetical order
97
Refactoring static void isWon(int pos) { // mark winning squares as true win for (int i = 0 ; i < DONE ; i++) { if ((i & pos) == pos) { won[i] = true; } // end if (i & pos) } // end for } // end isWon I move comments to save real estate
98
Refactoring static { // initialize winning squares by shifting a one n bits isWon((1 << 0) | (1 << 1) | (1 << 2)); // row one win isWon((1 << 3) | (1 << 4) | (1 << 5)); // row two win isWon((1 << 6) | (1 << 7) | (1 << 8)); // row three win isWon((1 << 0) | (1 << 3) | (1 << 6)); // col one win isWon((1 << 1) | (1 << 4) | (1 << 7)); // col two win isWon((1 << 2) | (1 << 5) | (1 << 8)); // col three win isWon((1 << 0) | (1 << 4) | (1 << 8)); // dia right win isWon((1 << 2) | (1 << 4) | (1 << 6)); // dia left win } // end static Document all possible statements
99
Refactoring 4.Perform Refactorings 1. Rename Method 2. Rename Constants 3. Rename Variables
100
Refactoring Summary: The name of a method does not reveal its purpose Change the name of the method. Rename Method
101
Refactoring Rename Method: Motivation: Methods should be named in a way the communicates their intension. Think what the comment for the method would be and turn that comment into the name of the method.
102
Refactoring Rename Method: Customer ____________ getinvcrelmt () Customer ____________ getInvoiceCreditLimit()
103
Refactoring Rename Method: Mechanics: Check if method signature is implemented by a super or sub class. If so perform these steps for each implementation. Declare new method with new name and copy old body into new method. Change body of old so it calls the new one. Find references to old and change them to refer to new one. Remove old method. Compile and test.
104
Refactoring CONSTANTS static final int CONTINUE = 0; // OLD OK status for game continues static final int ENDINGSTATE=(1<< 9)-1; //OLD DONE 111 111 111 Change these two constants to make code clearer…… TEST BETWEEN CHANGES. Rename constants, variables methods
105
Refactoring if (won[black]) { return LOSE; } if ((black | white) == DONE) { return STALEMATE; } return OK; BECOMES if (won[black]) { return LOSE; } if ((black | white) == DONE) { return STALEMATE; } return CONTINUE; TEST BETWEEN CHANGES.
106
Refactoring VARIABLES Image crossImage; // user image Image notImage; // computer image BECOMES computerImage = getImage(getCodeBase(), "oimage.gif"); userImage = getImage(getCodeBase(), "ximage.gif"); int white; // White's current position. The computer is white. int black; Black's current position. The user is black. BECOMES int userStatus; //user bitmask denotes user squares occupied OLD BLACK int computerStatus; //computer bitmask denotes squares occupied WHITE Rename constants, variables methods
107
Refactoring if ((white & (1 << i)) != 0) { g.drawImage(notImage, c*xoff + 1, r*yoff + 1, this); } else if ((black & (1 << i)) != 0) { g.drawImage(crossImage, c*xoff + 1, r*yoff + 1, this); BECOMES if ((computerStatus & (1 << i)) != 0) { // if computer square taken g.drawImage(computerImage, c*xoff + 1, r*yoff + 1, this); } else if ((userStatus & (1 << i)) != 0) { // if user square taken g.drawImage(userImage, c*xoff + 1, r*yoff + 1, this);
108
Refactoring VARIABLES int bestMove(int white, int black) BECOMES int bestMove(int computerStatus,int userStatus) { //compute best move int pw = white | (1 << mw); BECOMES int pw = computerStatus | (1 << mw); int pb = black | (1 << mb); BECOMES int pb = userStatus | (1 << mb);
109
Refactoring white = black = 0; BECOMES computerStatus = userStatus = 0; white |= 1 << (int)(Math.random() * 9); BECOMES computerStatus |= 1 << (int)(Math.random() * 9); white |= 1 << best; BECOMES computerStatus |= 1 << best;
110
Refactoring int best = bestMove(white, black); BECOMES int best = bestMove(computerStatus, userStatus); if (((black|white)&(1<< m))!= 0) black |= 1 << m; BECOMES if (((userStatus|computerStatus)&(1<< m))!= 0) userStatus |= 1 << m; if (((white&(1<< mw))== 0) &&((black&(1<<mw))== 0)) BECOMES if (((computerStatus&(1<< mw))== 0) &&((userStatus&(1<<mw))== 0))
111
Refactoring if ((black | white) == DONE) BECOMES if ((userStatus | computerStatus) == DONE) if (won[white]) return WIN; BECOMES if (won[computerStatus]) return WIN; if (won[black]) return LOSE; BECOMES if (won[userStatus]) return LOSE; if ((black | white) == DONE) BECOMES if ((userStatus | computerStatus) == DONE)
112
Refactoring MORE VARIABLES static int moves[] = {4,0,2,6,8,1,3,5,7}; BECOMES static int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7}; int mw = moves[i]; BECOMES int mw = mostStrategicMove[i]; Rename constants, variables methods
113
Refactoring won[] = new boolean[1 << 9]; BECOMES winningState[] = new boolean[1 << 9]; // winning states of game if (won[white]) BECOMES if (winningState[computerStatus]) if (won[black]) BECOMES if (winningState[userStatus]) if (won[pw]) BECOMES if (winningState[pw])
114
Refactoring METHODS boolean myMove(int m) { boolean yourMove() { BECOMES boolean legalComputerMove() { boolean legalUserMove(int m) { if (myMove()) if (yourMove(c + r * 3)) BECOMES if (legalComputerMove()) if (legalUserMove(c + r * 3)) Rename constants, variables methods
115
Refactoring OTHERS mw BECOMES int potentialComputerMove = mostStrategicMove[i]; pb BECOMES int potentialUserStatus = userStatus | (1 << j); c and r BECOMES row and col Rename constants, variables methods
116
Refactoring loop: for (int i = 0 ; i < 9 ; i++) { int mw = moves[i]; if (((white & (1 << mw)) == 0) && ((black & (1 << mw)) == 0)) { int pw = white | (1 << mw); if (won[pw]) {return mw; } // white wins, take it! for (int mb = 0 ; mb < 9 ; mb++) { if (((pw & (1 << mb)) == 0) && ((black & (1 << mb)) == 0)) { int pb = black | (1 << mb); if (won[pb]) {continue loop; } // black wins, take another } A big difference BEFORE
117
Refactoring loop: for (int i = 0 ; i < 9 ; i++) { // for square = 0 to < 9 int potentialComputerMove = mostStrategicMove[i]; if (((computerStatus & (1 << potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0)) { int potentialComputerStatus = computerStatus | (1 << potentialComputerMove); if (winningState[potentialComputerStatus]) { // computer wins return potentialComputerMove; } /// end if (not user taken ) && ( not computer taken ) A big difference AFTER
118
Refactoring // the computer did not find a winning move for (int j = 0 ; j < 9 ; j++) { // for square = 0 to < 9 if (((potentialComputerStatus & (1 << j)) == 0) && ((userStatus & (1 << j)) == 0)) { int potentialUserStatus = userStatus | (1 << j); if (winningState[potentialUserStatus]) { // user wins, take another continue loop; } // end if won } // end if && } // end for // found a move but user would win A big difference
119
Refactoring // computer has not found a winning move if (bestmove == -1) { // neither can win, this move will do bestmove = potentialComputerMove; } // end if } // end if && } // end for if (bestmove != -1) { // if no move found return the best one return bestmove; } // end if bestmove A big difference
120
Refactoring // there is no winning or good move – take mostStrategic move for (int i = 0 ; i < 9 ; i++) { // no great move, try first one open int firstAvailableComputerMove = mostStrategicMove[i]; if (((computerStatus&(1<< firstAvailableComputerMove))== 0) && (userStatus & (1 << firstAvailableComputerMove)) == 0)) { return firstAvailableComputerMove; } // end if && } // end for return -1; // return no more moves } // end best move A big difference
121
Refactoring Encapsulate
122
Refactoring Process 1. Encapsulate ALL class variables 2. Unit test 3. Functionally test
123
Refactoring 1. Encapsulate ALL class variables A.Write get and set methods for ALL class variables Modify ALL references to class variables to use get and set methods. Test methods.
124
Refactoring public int getComputerStatus () { return computerStatus; } public Image getUserImage (){return userImage;} public boolean getFirst () { return first; } public Image getComputerImage () { return computerImage;} public void setComputerStatus (int computerStatus) { this.computerStatus = computerStatus; } public void setUserImage (Image userImage) { this.userImage = userImage;} public void setFirst (boolean first) { this.first = first; } public void setCommputerImage (Image computerImage) { this.computerImage = computerImage; } Write getters and setters for all class variables
125
Refactoring Perform other encapsulation refactorings 1. Self encapsulating field 2. Encapsulate field 3. Encapsulate collection These are not covered in detail
126
Refactoring // GETS AND SETS ADDED TO CODE public int getComputerStatus () { return computerStatus; } public Image getUserImage (){return userImage;} public boolean getFirst () { return first; } public Image getComputerImage () { return computerImage;} …… public void setComputerStatus (int computerStatus) { this.computerStatus = computerStatus; } public void setUserImage (Image userImage) { this.userImage = userImage;} public void setFirst (boolean first) { this.first = first; } public void setCommputerImage (Image computerImage) { this.computerImage = computerImage; }
127
Refactoring //METHODS that need changes in their access to variables int bestMove(int computerStatus, int userStatus) { has parameters making them local variables – scoping? ok boolean legalUserMove(int canidateMove) { boolean legalComputerMove() { int gameStatus(int computerStatus, int userStatus) { public void paint(Graphics g) { // paint the screen public void mouseReleased(MouseEvent e) {
128
Refactoring //METHODS that need changes in their access to variables computerStatus = userStatus = 0; BECOMES setComputerStatus(0); setUserStatus(0); if ((userStatus | computerStatus) == ENDINGSTATE) { BECOMES if ((getUserStatus() | getComputerStatus()) == ENDINGSTATE) { computerImage = getImage(getCodeBase(), "oimage.gif"); BECOMES setComputerImage(getImage(getCodeBase(), "oimage.gif"));
129
Refactoring // domain functionality and GUI switch (gameStatus()) { // determine status case WIN: case LOSE: case STALEMATE: play(getCodeBase(), "audio/return.au"); computerStatus = userStatus = 0; if (first) { // reset first computerStatus |= 1 << (int)(Math.random() * 9); }// end if first = !first; repaint(); // GUI controlling when to display // RED LINED code NEEDS TO BE A METHOD TO TEST
130
Refactoring Make it a METHOD public void resetFirst() { if (getComputerFirst()) { // reset who is first setComputerStatus ( 1 << (int)(Math.random() * 9)); }// end if setComputerFirst (!getComputerFirst()); } // end resetStatus
131
Refactoring Call the METHOD Now this is all GUI code switch (gameStatus()) { // determine status case WIN: case LOSE: case STALEMATE: play(getCodeBase(), "audio/return.au"); resetStatus(); repaint(); return; } // end switch
132
Refactoring Constants and Variables
133
Refactoring Process 1. Review scope of all constants 2. Review scope of all variables 3. Adjust any gets/sets due to reviews 4. Apply refactorings
134
Refactoring 1. Review the scope of all constants 2. Review the scope of all variables 3. Adjust code to limit scope A.Review all the constants and variables to make sure they are not MORE global in scope than necessary. Downcast any that are too broad in scope. Adjust gets and sets to adapt to new or modified scope.
135
Refactoring 3. Adjust code to limit scope Investigate variables, parameters, and methods to see if they can be local or need to be uplifted to later evaluate their partitioning into classes. 1 2 3
136
Refactoring 3. Adjust code to limit scope private static int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7}; // square order of importance BECOMES int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7};// square order of importance // in the method bestMove
137
Refactoring 4.Apply Refactorings Replace Magic Number with Symbolic Constant Introduce Explaining Variable Split Temporary Variable Replace Temp with Query Separate Query from Modifier **** Replace Array with Object ***** Not all of these are covered in detail.
138
Refactoring Summary: You have a literal number with a particular meaning Create a constant, name it after the meaning, and replace the number with it. Replace Magic Number with Symbolic Constant
139
Refactoring Replace Magic Number with Symbolic Constant: Motivation: Magic numbers are numbers with special values that usually are not obvious. Add a constant to store the values of these magic numbers.
140
Refactoring Replace Magic Number with Symbolic Constant: Example: double potentialEnergy (double mass, double height) { return mass * 9.81 * height; }// end potential Energy double potentialEnergy (double mass, double height { return mass * GRAVITATIONAL_CONSTANT * height; }// end potentialEnergy static Final double GRAVITATIONAL_CONSTANT * 9.81; Should be
141
Refactoring Replace Magic Number with Symbolic Constant: Mechanics: declare a constant and set to magic number find occurrences of magic number if magic matches constant usage – use constant when all magic numbers changed compile and test.
142
Refactoring Replace Magic Number with Symbolic Constant: static final int ENDINGSTATE = (1 << 9) - 1; BECOMES static final int NINE_SHIFTING_BITS = 9; static final int ENDINGSTATE = (1 << NINE_SHIFTING_BITS) - 1; for (int i = 0 ; i < 9 ; i++) { // for square = 0 to < 9 BECOMES static final int firstCell = 0; // first cell at row 1 col 1 static final int lastCell = 8; // last cell at row 3, col 3 for (int i = firstCell ; i <= lastCell ; i++) { \
143
Refactoring Replace Magic Number with Symbolic Constant: int bestmove = -1; if (bestmove == -1) { if (bestmove != -1) { return -1; // return no more moves BECOMES static final int bestMoveNotFound = -1; //indicating best move not found int bestmove = bestmoveNotFound; if (bestmove == bestMoveNotFound) { if (bestmove != bestMoveNotFound) { return bestMoveNotFound; // return no more moves
144
Refactoring Replace Magic Number with Symbolic Constant: for (int row = 0 ; row < 3 ; row++) { for (int col = 0 ; col < 3 ; col++, i++) { int col = (x * 3) / d.width;// determine col int row = (y * 3) / d.height; // determine the row BECOMES static final int NUMBER_OF_COLUMNS = 3; static final int NUMBER_OF_ROWS = 3; for (int row = 0 ; row < NUMBER_OF_ROWS ; row++) { for (int col = 0 ; col < NUMBER_OF_COLUMNS ; col++, i++) { int col = (x * NUMBER_OF_COLUMNS) / d.width;// determine col int row = (y * NUMBER_OF_ROWS) / d.height; // determine the row
145
Refactoring Summary: You have a complicated expression. Put the result of the expression in a temp with a name that explains the purpose. 4. Introduce Explaining Variable
146
Refactoring 4. Introduce Explaining Variable: Motivation: Complex code that requires many lines of code but could more easily be explained with some variable name that helps to explain the code semantics.
147
Refactoring 4. Introduce Explaining Variable: Example: If ( (platform.toUpperCase().indexOf(“MAC”) > -1) && (browser.toUpperCase().indexOf(“IE”) > -1) && wasInitialized () && resize >0) { ….. other code …..} goes to final boolean isMacOs = platform.toUpperCase().indexOf(“MAC”) > -1; final boolean isIEBrowser = platform.toUpperCase().indexOf(“IE”) > -1; final boolean wasResized = resize >0; If (isMacOs && isIEBrowser && wasInitialized () && was Resized) { ….. other code …..}
148
Refactoring 4. Introduce Explaining Variable: Mechanics: declare a final temp variable set it to the result portion of the expression compile and test
149
Refactoring Summary: You have one temp assigned to more than once and it is not a loop variable. Make a new temp for each assignment 5. Split Temporary Variables
150
Refactoring 5. Split Temporary Variables: Motivation: Looping variables (control variables of a loop) or accumulator variables (summing variables) may have the need for assignments more than once. Other variables should not be assigned to more than once. Investigation of greater than one assignments to the same variable may yield variables that are actually used for different semantics.
151
Refactoring 5. Split Temporary Variables: Example: double getDistanceTravelled (int time) { double result; double acc = _primaryForce / _mass; // initial value of acceleration of first force int primaryTime = Math.min(time, _delay); result = 0.5 * acc * primaryTime * primaryTime; int secondaryTime = time - _delay; if (secondaryTime > 0) { double primaryVel = acc * _delay; acc = (_primaryForce * _secondaryForce) / _mass; // final value of acceleration result += primaryVel * secondaryTime + 0.5 *acc * secondaryTime * secondaryTime; } // end if return result; } // end getDistanceTravelled
152
Refactoring 5. Split Temporary Variables: Example: double getDistanceTravelled (int time) { double result; double primaryacc = _primaryForce / _mass; // initial value of acceleration of first force int primaryTime = Math.min(time, _delay); result = 0.5 * primaryacc * primaryTime * primaryTime; int secondaryTime = time - _delay; if (secondaryTime > 0) { double primaryVel = acc * _delay; double acc = (_primaryForce * _secondaryForce) / _mass; // final value of acceleration result += primaryVel * secondaryTime + 0.5 *acc * secondaryTime * secondaryTime; } // end if return result; } // end getDistanceTravelled CAN YOU THINK OF OTHER REFACTORING?
153
Refactoring 5. Split Temporary Variables: Mechanics: find the variables assigned to more than once change the name for each different assignments change references compile and test
154
Refactoring Summary: You are using a temporary variable to hold the result of an expression. Extract the expression into a method Replace all references to the temp with the expression. 3. Replace Temp with Query
155
Refactoring 3. Replace Temp with Query: Motivation: Methods with many temps tend to be long. Replacing the temp variable with a query method makes the code cleaner for further refactoring.
156
Refactoring 3. Replace Temp with Query: Example: double getPrice(){ int basePrice = _quanity * itemPrice; double discountFactor; if (basePrice > 1000) discountFactor = 0.95 else discountFactor = 0.98; return basePrice *discountFactor; }// end getPrice
157
Refactoring 3. Replace Temp with Query: Example: double getPrice(){ int basePrice = _quanity * itemPrice; double discountFactor; if (basePrice() > 1000) discountFactor = 0.95 else discountFactor = 0.98; return basePrice() *discountFactor; }// end getPrice private int basePrice () { return quanity * itemPrice; }
158
Refactoring 3. Replace Temp with Query: Example: double getPrice(){ double discountFactor = discountFactor(); if (basePrice() > 1000) discountFactor = 0.95 else discountFactor = 0.98; return basePrice() *discountFactor; }// end getPrice private double discountFactor () { if (basePrice() > 1000) return 0.95; else return 0.98; } // end discountFactor Extract Method
159
Refactoring 3. Replace Temp with Query: Example: double getPrice(){ double discountFactor = discountFactor(); return basePrice() *discountFactor; }// end getPrice private double discountFactor () { if (basePrice() > 1000) return 0.95; else return 0.98; } // end discountFactor private int basePrice () { return quanity * itemPrice; } Can I do more? Replace Temp with Query
160
Refactoring 3. Replace Temp with Query: Mechanics: declare the temp as final to check references compile to test extract the right hand side as a method body if a loop take entire loop name and construct the method replace the temp with the call
161
Refactoring 3. Replace Temp with Query: int best = bestMove(computerStatus, userStatus); setComputerStatus (computerStatus | 1 << best); BECOMES setComputerStatus (computerStatus | 1 << bestMove(computerStatus, userStatus));
162
Refactoring Methods
163
Refactoring Learning objective – “good” object-oriented methods. Primary refactoring used to produce “good” methods is Extract Method, but others are covered.
164
Refactoring “Good” method is defined as a method with an acceptable level of cohesion [Kwang99,Smith04]
165
Refactoring “Good” method is further defined as a method acceptably cohesive less than 20 lines in length accomplishes one functionality testable with one unit test method
166
Refactoring Kang [KAN99, SMI04] defines levels of cohesion which are ranked from the best level to the worst. Functional, sequential and communicational cohesion in methods is an acceptable level.
167
Refactoring Functional cohesion deals with the ability of a method to produce only one output (i.e., to change the state of only one member variable) and is considered the best level of cohesion.
168
Refactoring In sequential cohesion, more than one variable is modified; however, all modifications result in the change to only one member variable. The outputs of one or more modifications are the inputs to the modification of the member variable.
169
Refactoring In communicational cohesion, multiple outputs are dependent on a common input but are not derived in a loop or selection statement. Not as good as functional or sequential, refactor if possible.
170
Refactoring Method sizes may be very small as a natural consequence of good object- oriented design [Lieberherr89]. Studies have shown that larger methods result in a reduction in understandability, reuse, and maintainability. [Hudli94, Lorenz94, McCabe94, Tegaden92].
171
Refactoring Refactorings used Extract Method Inline Method
172
Refactoring Summary: - most key refactoring method You have a cluster of code which can be grouped into a method to simplify the code. Turn the cluster in to a method with a name that explains the function of the method. 1. Extract Method
173
Refactoring 1. Extract Method: Motivation: Method is too long, needs comments to understand. Need very small methods returning one value. Method names are allowed to be longer than the code itself.
174
Refactoring 1. Extract Method: Example: no local variables void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; // print banner System.our.println (“*************”); System.our.println (“Customer Owes”) System.our.println (“*************”); // caculate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); // print details System.out.println (“name:” + _name); System.out.println (“amount” + outstanding); }
175
Refactoring 1. Extract Method: Example: no local variables void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner(); // cacluate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); // print details System.out.println (“name:” + _name); System.out.println (“amount” + outstanding); … Void printBanner() { System.our.println (“*************”); System.our.println (“Customer Owes”) System.our.println (“*************”); } // end printBanner }
176
Refactoring 1. Extract Method: Example: locals only referenced void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner() // cacluate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); // print details System.out.println (“name:” + _name); System.out.println (“amount” + outstanding); Void printBanner() { System.our.println (“*************”); System.our.println (“Customer Owes”) System.our.println (“*************”); } // end printBanner }
177
Refactoring 1. Extract Method: Example: locals only referenced pass in needed data as parameters void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner() // cacluate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); printDetails (outstanding); …. Void printDetails (double outstanding) { System.out.println (“name:” + _name); System.out.println (“amount” + outstanding); ……… }
178
Refactoring 1. Extract Method: Example: one local updated void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner() // cacluate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); printDetails (outstanding); Void printDetails (double outstanding) { System.out.println (“name:” + _name); System.out.println (“amount” + outstanding); ……… }
179
Refactoring 1. Extract Method: Example: one local updated return updated local void printOwing() { printBanner() double outstanding = getOutstanding(); printDetails (outstanding); Double getOutstanding() { Enumeration e = _orders.elements(); double outstanding = 0.0; // cacluate outstanding while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } // end while return outstanding; } // end getOutstanding ……… }
180
Refactoring 1. Extract Method: Mechanics locate clusters of actual code copy to a new method if local variables needed define parameters if local variable (one) updated make return type build call if local variable updated (more than one) make multiple methods or split variables build calls
181
Refactoring Summary: You have a method body that is just as clear as the name of the method Turn the method into inline code rather than a method. 2. Inline Method
182
Refactoring 2. Inline Method: Motivation: The body is just as clear as a method name and there exist needless indirection.
183
Refactoring 2. Inline Method: Example: int getRating ()} return (moreThanFiveLateDeliveries () ? 2:1; }// end getRating boolean moreThanFiveLateDeliveries() { return _numberofLateDeliveries > 5; } // end moreThanFiveLateDeliveries
184
Refactoring 2. Inline Method: Example: int getRating ()} return _numberofLateDeliveries > 5 ? 2:1; } // end getRating
185
Refactoring 2. Inline Method: Mechanics: find calls to the method replace with inline code test remove method definition
186
Refactoring ????init(…) ???? destroy(…) ????legalComputerMove(…) ???? legalUserMove (…) ???? gameStatus (…) ???? resetFirst(…) ????paint (…) ????mouseReleased(…) ???? bestMove(…) - more than 20 lines *acceptably cohesive *less than 20 lines *accomplishes one functionality *testable with one unit test * Indicates that it meets criteria
187
Refactoring ****init(…) public void init() { // initialize applet, load images, add listener setComputerImage(getImage(getCodeBase(), "oimage.gif")); setUserImage(getImage(getCodeBase(), "ximage.gif")); addMouseListener(this); } // end init *Cohesive acceptable - modifies two class variables indirectly *Size: acceptable *One functionality: TWO set up images and add listener – what if we wanted to implement a phone interface. *One unit test: neet two tests one for images and one for listenert
188
Refactoring ****destroy(…) public void destroy() { // destroy listener needed for closing removeMouseListener(this); } // end destroy *Cohesive – modifies no class variables - acceptable *Size – acceptable *One function – only one acceptable *One unit test – only one acceptable
189
Refactoring **** init(…) **** destroy(…) ????legalComputerMove(…) ???? legalUserMove (…) ???? gameStatus (…) ???? resetFirst(…) ????paint (…) ????mouseReleased(…) ???? bestMove(…) - more than 20 lines *acceptably cohesive *less than 20 lines *accomplishes one functionality *testable with one unit test
190
Refactoring ???? legalUserMove (…) boolean legalUserMove(int computerStatus, int userStatus, int canidateMove) { if ((canidateMove 8)) { return false; } // end if if (((userStatus | computerStatus) & (1 << canidateMove)) != 0) { return false;} // end if setUserStatus (userStatus | 1 << canidateMove); return true; } // end legalUserMove *Cohesive – modifies userStatus indirectly - acceptable *Size – acceptable *Only One function – NO Test legal AND sets user status *Testable two types of tests for two function LOOK at where this method is callled to see if we can separate.
191
Refactoring ???? legalUserMove (…) boolean legalUserMove(int computerStatus, int userStatus, int canidateMove) { if ((canidateMove 8)) { return false; } // end if if (((userStatus | computerStatus) & (1 << canidateMove)) != 0) {return false;} // end if setUserStatus (userStatus | 1 << canidateMove); return true; } // end legalUserMove if (legalUserMove(getComputerStatus(), getUserStatus(), col + row * 3)) { repaint(); …….
192
Refactoring **** legalUserMove (…) boolean legalUserMove(int computerStatus, int userStatus, int canidateMove) { // user move return true if legal move if ((canidateMove 8)) { return false;} // end if if (((userStatus | computerStatus) & (1 << canidateMove)) != 0) { return false; } // end if return true; } // end legalUserMove int canidateMove = col + row * 3; if (legalUserMove(getComputerStatus(), getUserStatus(), canidateMove)) { setUserStatus (userStatus | 1 << canidateMove);
193
Refactoring ????legalComputerMove(…) boolean legalComputerMove(int computerStatus, int userStatus) { if ((userStatus | computerStatus) == ENDINGSTATE) {return false;} //end if setComputerStatus(computerStatus|1<<bestMove(computerStatus,userStatus)); return true; } // end legalComputerMove Cohesive = modifies computerStatus indirectly -- acceptable Size – acceptable One function – NO it test if legal AND sets computer status Testing - two types of tests one for the legality and one for set Again LOOK at where this method is called to see if we can separate
194
Refactoring ****legalComputerMove(…) boolean legalComputerMove(int computerStatus, int userStatus) { if ((userStatus | computerStatus) == ENDINGSTATE) {return false;} // end if setComputerStatus (computerStatus | 1 << bestMove(computerStatus, userStatus)); return true; } // end legalComputerMove if (legalComputerMove(getComputerStatus(), getUserStatus())) { repaint(); ………
195
Refactoring ****legalComputerMove(…) boolean legalComputerMove(int computerStatus, int userStatus) { if ((userStatus|computerStatus)==ENDINGSTATE) {return false; } // end if return true; } // end legalComputerMove if (legalComputerMove(getComputerStatus(), getUserStatus())) { setComputerStatus (computerStatus | 1 << bestMove(getComputerStatus(), getUserStatus()));
196
Refactoring **** init(…) **** destroy(…) ****legalComputerMove(…) ****legalUserMove (…) ???? gameStatus (…) ???? resetFirst(…) ????paint (…) ????mouseReleased(…) ???? bestMove(…) - more than 20 lines *acceptably cohesive *less than 20 lines *accomplishes one functionality *testable with one unit test
197
Refactoring **** gameStatus (…) int gameStatus(int computerStatus, int userStatus ) { if (winningState[computerStatus]) { return WIN;} // end if if (winningState[userStatus]) { return LOSE;} // end if if ((userStatus | computerStatus) == ENDINGSTATE) { return STALEMATE; } // end if return CONTINUE; } // end gameStatus Cohesive = no modifications -- acceptable Size – acceptable One function – acceptable Testing - one test – acceptable (4 test needed.
198
Refactoring ???? resetFirst(…) public boolean resetFirst (boolean computerFirst) { if (computerFirst) { // reset who is first setComputerStatus ( 1 << (int)(Math.random() * 9)); }// end if return !computerFirst; } // end resetStatus Cohesive = modifies one indirectly -- acceptable Size – acceptable One function – NO resets first AND sets computer status Testing - two types of tests one for reset and one for the set LOOK at where this method is called to see if we can separate
199
Refactoring **?? resetFirst(…) public boolean resetFirst (boolean computerFirst) { if (computerFirst) { // reset who is first setComputerStatus ( 1 << (int)(Math.random() * 9)); }// end if return !computerFirst; } // end resetStatus \ setComputerFirst (resetFirst(getComputerFirst()));
200
Refactoring **** resetFirst(…) if (computerFirst) { setComputerStatus ( 1 << (int)(Math.random() * 9)); }// end if setComputerFirst (!computerFirst); It makes more sense to take move the code back into the calling method and look (our case) or relook (if already evaluated) at the calling method for characteristics of good.
201
Refactoring **** init(…) **** destroy(…) ****legalComputerMove(…) ****legalUserMove (…) **** gameStatus (…) **** resetFirst(…) ????paint (…) ????mouseReleased(…) ???? bestMove(…) - more than 20 lines *acceptably cohesive *less than 20 lines *accomplishes one functionality *testable with one unit test
202
Refactoring ????paint (…) public void paint(Graphics g) { // paint the screen Dimension d = getSize(); g.setColor(Color.black); int xoff = d.width / NUMBER_OF_ROWS; int yoff = d.height / NUMBER_OF_COLUMNS; g.drawLine(xoff, 0, xoff, d.height); // draw first horiz line g.drawLine(2*xoff, 0, 2*xoff, d.height); // draw second line g.drawLine(0, yoff, d.width, yoff); // draw first verticle line g.drawLine(0, 2*yoff, d.width, 2*yoff); // draw second line
203
Refactoring ????paint (…) **continued int i = 0; for (int row = 0 ; row < NUMBER_OF_ROWS ; row++) {draw images for (int col = 0 ; col < NUMBER_OF_COLUMNS ; col++, i++) { if ((getComputerStatus() & (1 << i)) != 0) { // if square take g.drawImage(getComputerImage(), col*xoff + 1,row*yoff + 1, this); } else if ((getUserStatus() & (1 << i)) != 0) { // if user square taken g.drawImage(getUserImage(), col*xoff + 1, row*yoff + 1, this); } // end if }// end for } // end for } // end paint
204
Refactoring *??*paint (…) **continued Cohesive – sets no class variables – acceptable Size – somewhat long Functionality - two exist 1) draw the grid 2) draw the images Testing – would need two tests for each each function except both are gui so these are functional tests SO we will separate the functions
205
Refactoring THE NEW methods public void drawGrid(Graphics g, Dimension d, int xoff, int yoff){ g.drawLine(xoff, 0, xoff, d.height); // draw first horizontal line g.drawLine(2*xoff, 0, 2*xoff, d.height); // draw second horizontal line g.drawLine(0, yoff, d.width, yoff); // draw first verticle line g.drawLine(0, 2*yoff, d.width, 2*yoff); // draw second verticle line } // end drawGrid
206
Refactoring THE NEW methods public void drawImages(Graphics g, Dimension d, int xoff, int yoff){ int i = 0; for (int row = 0 ; row < NUMBER_OF_ROWS ; row++) { // draw computer and user images for (int col = 0 ; col < NUMBER_OF_COLUMNS ; col++, i++) { if ((getComputerStatus() & (1 << i)) != 0) { // if computer bitmask square taken g.drawImage(getComputerImage(), col*xoff + 1, row*yoff + 1, this); } else if ((getUserStatus() & (1 << i)) != 0) { // if user bitmask square taken g.drawImage(getUserImage(), col*xoff + 1, row*yoff + 1, this); } // end if }// end for } // end for } // end draw Images
207
Refactoring ****paint (…) THE NEW paint public void paint(Graphics g) { // paint the screen Dimension d = getSize(); g.setColor(Color.black); int xoff = d.width / NUMBER_OF_ROWS; int yoff = d.height / NUMBER_OF_COLUMNS; drawGrid(g, d, xoff, yoff); drawImages (g, d, xoff, yoff); }// end paint
208
Refactoring **** init(…) **** destroy(…) ****legalComputerMove(…) ****legalUserMove (…) **** gameStatus (…) **** resetFirst(…) ****paint (…) ????mouseReleased(…) ???? bestMove(…) - more than 20 lines *acceptably cohesive *less than 20 lines *accomplishes one functionality *testable with one unit test
209
Refactoring ????mouseReleased(…) public void mouseReleased(MouseEvent e) { // user clicked applet int x = e.getX(); // get mouse x location int y = e.getY(); // get mouse y location Many functions are included in this method and we will extract this functionality in methods
210
Refactoring // this code checks if the game is over on the previous move switch (gameStatus(getComputerStatus(), getUserStatus())) { case WIN: case LOSE: case STALEMATE: play(getCodeBase(), "audio/return.au"); setComputerStatus(0); setUserStatus(0); if (computerFirst) { setComputerStatus ( 1 << (int)(Math.random() * 9)); }// end if setComputerFirst (!computerFirst); repaint(); return; } // end switch
211
Refactoring // find out where the click occurred Dimension d = getSize(); int col = (x * NUMBER_OF_COLUMNS) / d.width; int row = (y * NUMBER_OF_ROWS) / d.height;
212
Refactoring // determine if user move causes a win or stalemate if (legalUserMove(getComputerStatus(),getUserStatus(),col+row*3)) { repaint(); switch (gameStatus(getComputerStatus(), getUserStatus())) { case WIN: System.out.println ("I win"); break; case LOSE: System.out.println ("You win"); break; case STALEMATE: System.out.println ("No Winner"); break; ………
213
Refactoring // find a legal computer move and see if it wins or stales default: if (legalComputerMove(getComputerStatus(), getUserStatus())) { repaint(); switch (gameStatus(getComputerStatus(), getUserStatus())) { case WIN: System.out.println ("I win this move"); break; case LOSE: System.out.println ("You win this move"); break; case STALEMATE: System.out.println ("No Winner this move"); break; default: } // end if } else { play(getCodeBase(), "audio/beep.au"); } // end else } // end default } else { play(getCodeBase(), "audio/beep.au"); }// end else } // end mouseReleased
214
Refactoring ????mouseReleased(…) Cohesive – sets computerStatus, computerFirst, userStatus Size – somewhat long Functionality - many functions some may be reusable Testing – would need many tests SO we will separate the functions
215
Refactoring ????gameOver(…) public boolean gameOver(int status) { // check and reset if a WIN, LOSE, or STALEMATE after computer move switch (status) { case WIN: case LOSE: case STALEMATE: play(getCodeBase(), "audio/return.au"); setComputerStatus(0); setUserStatus(0); if (computerFirst) { setComputerStatus ( 1 << (int)(Math.random() * 9)); }// end if setComputerFirst (!computerFirst); repaint(); return true; default: return false; } // end switch } // end gameOver *Cohesive – NO all sets here *Size - acceptables *One functionality - no many *Testable – no many
216
Refactoring ????resetGame(…) public void resetGame () { play(getCodeBase(), "audio/return.au"); setComputerStatus(0); setUserStatus(0); if (computerFirst) { setComputerStatus ( 1 << (int)(Math.random() * 9)); }// end if setComputerFirst (!computerFirst); } // end resetGame *Cohesive – NO all sets here *Size - acceptable *One functionality - no many *Testable – yes only one not a set
217
Refactoring ****playComputerIffirst(…) public void playComputerIfFirst ( ) { if (getComputerFirst()) { setComputerStatus ( 1 << (int)(Math.random() * 9)); }// end if } // end playComputerIfFirst *Cohesive – only indirectly sets one class variable *Size - acceptable *One functionality - yes *Testable – yes
218
Refactoring ****resetGame(…) public void resetGame () { play(getCodeBase(), "audio/return.au"); setComputerStatus(0); setUserStatus(0); playComputerifFirst ( ); setComputerFirst (!getComputerFirst()); } // end resetGame *Cohesive – acceptable ONLY sets *Size - acceptable *One functionality - only sets *Testable – yes sets
219
Refactoring ????mouseReleased(…) new version 2 public void mouseReleased(MouseEvent e) { // user clicked applet int x = e.getX(); // get mouse x location int y = e.getY(); // get mouse y location Dimension d = getSize(); int col = (x * NUMBER_OF_COLUMNS) / d.width; // determine col int row = (y * NUMBER_OF_ROWS) / d.height; // determine row if (gameOver (gameStatus(getComputerStatus(), getUserStatus()))) { resetGame(); repaint(); return; } // end if
220
Refactoring mouseReleased () VERSION 2 CONTINUED // determine if user move causes a win, loose or stalemate and post it if (legalUserMove(getComputerStatus(),getUserStatus(),col+row*3)) { repaint(); switch (gameStatus(getComputerStatus(), getUserStatus())) { case WIN: System.out.println ("I win"); break; case LOSE: System.out.println ("You win"); break; case STALEMATE: System.out.println ("No Winner"); break; ………
221
Refactoring // find a legal computer move and see if it wins or stales // VERSION 2 default: if (legalComputerMove(getComputerStatus(), getUserStatus())) { repaint(); switch (gameStatus(getComputerStatus(), getUserStatus())) { case WIN: System.out.println ("I win this move"); break; case LOSE: System.out.println ("You win this move"); break; case STALEMATE: System.out.println ("No Winner this move"); break; default: } // end if } else { play(getCodeBase(), "audio/beep.au"); } // end else } // end default } else { play(getCodeBase(), "audio/beep.au"); }// end else } // end mouseReleased
222
Refactoring ????postGameStatus(…) public void postGameStatus (int status) { switch (status) { case WIN: System.out.println ("I win #1"); break; case LOSE: System.out.println ("You win #1"); break; case STALEMATE: System.out.println ("No Winner #1"); break; default: } // end switch } // end checkGameStatus *Cohesive – no modifies acceptable *Size - acceptable *One functionality - yes acceptable *Testable – yes one type (3 tests)
223
Refactoring ????VERSION 3 of mouseReleased(…) public void mouseReleased(MouseEvent e) { // user clicked applet int x = e.getX(); // get mouse x location int y = e.getY(); // get mouse y location Dimension d = getSize(); int col = (x * NUMBER_OF_COLUMNS) / d.width; / determine col int row = (y * NUMBER_OF_ROWS) / d.height; // determine row if (gameOver (gameStatus(getComputerStatus(), getUserStatus()))) { resetGame(); repaint(); return; } // end if
224
Refactoring ????REMAINDER OF mouseReleased(…) VERSION 3 int canidateMove = col + row * 3; if (legalUserMove(getComputerStatus(), getUserStatus(), canidateMove)) { setUserStatus (userStatus | 1 << canidateMove); repaint(); int status = gameStatus(getComputerStatus(), getUserStatus()); if (!(status == CONTINUE)) { postGameStatus (status); } else { if (legalComputerMove(getComputerStatus(), getUserStatus())) { setComputerStatus(computerStatus|1<<bestMove(getComputerStatus(),getUserStatus())); repaint(); postGameStatus(gameStatus(getComputerStatus(), getUserStatus())); } else { play(getCodeBase(), "audio/beep.au"); } // end else } // end else } else { // not legal user move play(getCodeBase(), "audio/beep.au"); }// end else
225
Refactoring ???? mouseReleased(…) VERSION 3 *Cohesive – sets two class variables indirectly ? *Size – still somewhat large *One functionality - no several functions tries to moveUser and moveComputer *Testable – no several types per function
226
Refactoring ???? mouseReleased(…) VERSION 4 public void mouseReleased(MouseEvent e) { // user clicked applet int x = e.getX(); // get mouse x location int y = e.getY(); // get mouse y location Dimension d = getSize(); int col = (x * NUMBER_OF_COLUMNS) / d.width; get col int row = (y * NUMBER_OF_ROWS) / d.height; // determine row if (gameOver (gameStatus(getComputerStatus(), getUserStatus()))) { resetGame(); repaint(); return; } // end if
227
Refactoring ???? mouseReleased(…) VERSION 4 CONTINUED int canidateMove = col + row * 3; if (legalUserMove(getComputerStatus(), getUserStatus(), canidateMove)) { repaint(); setUserStatus (userStatus | 1 << canidateMove); int status = gameStatus(getComputerStatus(), getUserStatus()); if (status == CONTINUE) { if (legalComputerMove(getComputerStatus(), getUserStatus())) { repaint(); moveComputer(); } else { play(getCodeBase(), "audio/beep.au"); } // end else } else { postGameStatus (status); } // end else } else { play(getCodeBase(), "audio/beep.au"); }// end else // not legal } // end mouseReleased Still not good but best we can do
228
Refactoring **** init(…) **** destroy(…) ****legalComputerMove(…) ****legalUserMove (…) **** gameStatus (…) **** resetFirst(…) ****paint (…) **??mouseReleased(…) ???? bestMove(…) *acceptably cohesive *less than 20 lines *accomplishes one functionality *testable with one unit test
229
Refactoring ???? bestMove (…) int bestMove(int computerStatus, int userStatus) { int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7}; int bestmove = bestMoveNotFound; loop: for (int i = firstCell ; i <= lastCell ; i++) { int potentialComputerMove = mostStrategicMove[i]; if (((computerStatus & (1 << potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0)) { int potentialComputerStatus = computerStatus | (1 << potentialComputerMove); if (winningState[potentialComputerStatus]) { // computer wins return potentialComputerMove; } /// end if (not user taken ) && ( not computer taken )
230
Refactoring ???? bestMove (…) CONTINUED for (int j = firstCell ; j <= lastCell ; j++) { // for square = 0 to < 9 if (((potentialComputerStatus & (1 << j)) == 0) && ((userStatus & (1 << j)) == 0)) { int potentialUserStatus = userStatus | (1 << j); if (winningState[potentialUserStatus]) { // user wins, take another continue loop; } // end if won } // end if && } // end for if (bestmove == bestMoveNotFound) { // neither wins, this move will do bestmove = potentialComputerMove; } // end if } // end if && } // end for
231
Refactoring ???? bestMove (…) CONTINUED *Cohesive – sets no class variables *Size – somewhat large *One functionality - yes *Testable – one type (several tests)
232
Refactoring ???? userWins (…) public boolean userWins (int potentialComputerStatus) { for (int j = firstCell ; j <= lastCell ; j++) { // for square = 0 to < 9 if (((potentialComputerStatus & (1 << j)) == 0) && ((userStatus & (1 << j)) == 0)) { int potentialUserStatus = userStatus | (1 << j); if (winningState[potentialUserStatus]) { // user wins, take anothe return true; } // end if won } // end if && } // end for return false; } // end checkIfUserWon *Cohesive – no changes to class var *somewhat long *one function – yes one *testable - yes one type of test
233
Refactoring ???? bestMove (…) VERSION 2 int bestMove(int computerStatus, int userStatus) { int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7}; int bestmove = bestMoveNotFound; loop:
234
Refactoring ???? bestMove (…) VERSION 2 CONTINUED for (int i = firstCell ; i <= lastCell ; i++) { int potentialComputerMove = mostStrategicMove[i]; if (((computerStatus & (1 << potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0)) { int potentialComputerStatus = computerStatus | (1 << potentialComputerMove); if (winningState[potentialComputerStatus]) { // computer wins return potentialComputerMove; } /// end if (not user taken ) && ( not computer taken ) if (userWins (potentialComputerStatus)) { continue loop;} if (bestmove == bestMoveNotFound) { // neither can win bestmove = potentialComputerMove; } // end if } // end if && } // end for
235
Refactoring ???? bestMove (…) VERSION 2 CONTINUED if (bestmove != bestMoveNotFound) { // return best one return bestmove; } // end if bestmove for (int i = firstCell ; i <= lastCell ; i++) { // try first one open int firstAvailableComputerMove = mostStrategicMove[i]; if (((computerStatus & (1 << firstAvailableComputerMove)) == 0) && ((userStatus & (1 << firstAvailableComputerMove)) == 0)) { return firstAvailableComputerMove; } // end if && } // end for return bestMoveNotFound; // return no more moves } // end best move
236
Refactoring **** bestMove (…) CONTINUED *Cohesive – sets no class variables *Size – still somewhat large but tolerable *One functionality - yes *Testable – yes one type several tests
237
Refactoring REMEMBER For each new method that is not ONLY functionally tested, you will have to write a unit test.
238
Refactoring Conditionals
239
Refactoring Conditionals should not be too complex. If they are complex, refactor them to make simple methods making readability and understandability better.
240
Refactoring Learning objective – have simple conditionals which are self documenting with meaningful names
241
Refactoring In our program with explaining variable names, there are still some conditionals which are difficult to understand. We find them in mainly in bestMove().
242
Refactoring FOR EXAMPLE int bestMove(int computerStatus, int userStatus) { …… if (((computerStatus & (1 << potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0)) if (((potentialComputerStatus & (1 << j)) == 0) && ((userStatus & (1 << j)) == 0)) if (((computerStatus & (1 <<firstAvailableComputerMove)) == 0) && ((userStatus & (1 <firstAvailableComputerMove)) == 0)) {
243
Refactoring REFACTORINGS Decomposing Conditional Consolidating Conditional Statements Consolidate Duplicate Conditional Fragments Replace Nested Conditional with Guard Clauses Remove Control Flag Not all of these are covered in detail. Rather the resulting code for TTT is shown.
244
Refactoring if (((computerStatus & (1 << firstAvailableComputerMove)) == 0) && ((userStatus & (1 << firstAvailableComputerMove)) == 0)) public boolean cellEmpty (int computerStatus,int potentialComputerMove,int userStatus) { return ((computerStatus & (1 << potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0); } // end cellEmpty BECOMES
245
Refactoring if ((getComputerStatus() & (1 << i)) != 0) public boolean squareOccupied (int i, int playerStatus) { return ((playerStatus & (1 << i)) != 0); } // end squareOccupied BECOMES WITH if (squareOccupied (i, getComputerStatus())) REUSED 3 times
246
Refactoring Refactoring has gained much popularity during the last few years. There a website that defines all the refactorings at http://www.refactoring.com/catalog/index.html You will find that some of these will become commonplace in your code and some you will have to work hard to accomplish.
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.