Presentation is loading. Please wait.

Presentation is loading. Please wait.

Refactoring This lecture is divided into and introduction to refactoring and then several lessons. The intent is not to teach you all the refactorings.

Similar presentations


Presentation on theme: "Refactoring This lecture is divided into and introduction to refactoring and then several lessons. The intent is not to teach you all the refactorings."— Presentation transcript:

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.


Download ppt "Refactoring This lecture is divided into and introduction to refactoring and then several lessons. The intent is not to teach you all the refactorings."

Similar presentations


Ads by Google