Refactoring: Improving the Design of Existing Code © Martin Fowler, 1997 Martin Fowler.

Slides:



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

Software Testing and Maintenance 1 Today’s Agenda  Course Evaluation  HW 4 Return  HW 5 Correction  Quiz 4 Next Class  Software Refactoring.
1 Software Maintenance and Evolution CSSE 575: Session 2, Part 2 Composing Methods Steve Chenoweth Office Phone: (812) Cell: (937)
Refactoring Overview  What is refactoring?  What are four good reasons to refactor?  When should you refactor?  What is a bad smell (relative to refactoring.
1 Software Maintenance and Evolution CSSE 575: Session 2, Part 3 Moving Features Between Objects Steve Chenoweth Office Phone: (812) Cell: (937)
Refactoring Software Engineering Refactoring Software Engineering 2011 Department of Computer Science Ben-Gurion university Based on slides of: Mira Balaban.
Introduction to Refactoring Excerpted from ‘What is Refactoring?’ by William C. Wake and Refactoring: Improving the Design of Existing Code by Martin Fowler.
25-Jun-15 Refactoring III. General philosophy A refactoring is just a way of rearranging code Refactorings are used to solve problems If there’s no problem,
REFACTORING Improving the Design of Existing Code Atakan Şimşek e
Refactoring, continue. Announcements Due today Resubmit HW5 (feedback on HW5 in SVN) HW7 HW8 will be up tonight Due Saturday at 5:59! Quiz 9 Spring 15.
13-Jul-15 Refactoring II. Books Design Patterns is the classic book by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides Basically a catalog.
Test. A software house decides to develop a DVD renting program. The product manager identifies the following requirements: Every DVD will have a title,
REFACTORING Lecture 4. Definition Refactoring is a process of changing the internal structure of the program, not affecting its external behavior and.
1 v1.6 08/02/2006 Overview of Eclipse Lectures 1.Overview 2.Installing and Running 3.Building and Running Java Classes 4.Refactoring 5.Debugging 6.Testing.
CS5103 Software Engineering Lecture 13 Software Refactoring Software Licenses.
Refactoring – III Measured Smells. Smells Covered 1. Comments 2. Long method 3. Large Class 462.
Refactoring: Improving the Design of Existing Code © Martin Fowler, Martin Fowler.
Refactoring Cristescu Marilena. Definitions Loose Usage: Reorganize a program(or something) As a noun: a change made to the internal structure of some.
Refactoring Improving the structure of existing code Refactoring1.
Small changes to code to improve it. Refactoring Defined A change made to the internal structure of software to make it easier to understand and cheaper.
SWE 316: Software Design and Architecture Objectives Lecture # 20 Improving the existing design: Refactoring SWE 316: Software Design and Architecture.
Refactoring An Automated Tool for the Tiger Language Leslie A Hensley
Refactoring1 Improving the structure of existing code.
Refactoring Software Engineering Refactoring Software Engineering 2012 Department of Computer Science Ben-Gurion university Based on slides of: Mira Balaban.
CS4723 Software Engineering Lecture 12 Software Design Quality.
Refactoring Deciding what to make a superclass or interface is difficult. Some of these refactorings are helpful. Some research items include Inheritance.
Agile Software Development Lab Spring 2008 R O O T S 1 Sina Thomas ) XP: Extreme.
Refactoring: Improving the Design of Existing Code © Martin Fowler, Martin Fowler.
1 CSC/ECE 517 Fall 2010 Lec. 3 Overview of Eclipse Lectures Lecture 2 “Lecture 0” Lecture 3 1.Overview 2.Installing and Running 3.Building and Running.
Refactoring. Refactoring Overview  What is refactoring?  What are four good reasons to refactor?  When should you refactor?  What is a bad smell (relative.
Today’s Agenda  More refactoring patterns Software Testing and Maintenance 1.
Refactoring 2. Admin Blackboard Quiz Acknowledgements Material in this presentation was drawn from Martin Fowler, Refactoring: Improving the Design of.
REFACTORINGREFACTORING. Realities Code evolves substantially during development Requirements changes 1%-4% per month on a project Current methodologies.
Refactoring II Dealing with Polymorphism. Switch in Rental Switches on Movie! class Rental … public double getCharge() { double result = 0; switch (getMovie().getPriceCode()){
Design Patterns.
1 Software Maintenance and Evolution CSSE 575: Session 3, Part 3 Dealing with Generalization Steve Chenoweth Office Phone: (812) Cell: (937)
Refactoring. Announcements HW7 due today, HW8 coming up tomorrow (I’m taking a late day on posting HW8) Grades and feedback for HW0-5 in Homework Server.
Static?. Static Not dynamic class Widget { static int s; int d; // dynamic // or instance // variable }
SEG 4110 – Advanced Software Design and Reengineering Topic T Introduction to Refactoring.
CSSE 375 Organizing Data – Part 2 Shawn and Steve Continue the same quiz!
Refactoring1 Improving the structure of existing code.
Software Construction and Evolution - CSSE 375 Simplifying Conditionals Shawn & Steve.
Refactoring Constants and Variables Lesson Three: Constants and Variables.
CSSE 375 Organizing Data – Part 1 Shawn and Steve Q1.
Software Construction and Evolution - CSSE 375 Dealing with Generalization Steve and Shawn Left – In the 1990 movie “The Freshman,” Matthew Broderick,
Refactoring. DCS – SWC 2 Refactoring ”A change made to the internal structure of software to make it easier to understand and cheaper to modify without.
Refactoring: Improving the Design of Existing Code.
Refactoring Improving code after it has been written.
Module 9. Dealing with Generalization Course: Refactoring.
Catalog of Refactoring (1) Composing Methods. Code Smells Long methods Dubious temporary variables Dubious methods.
ICONFINDER ICONFINDER Founded Django based web application -PostgreSQL -Elasticsearch -Amazon Elastic Compute.
Catalog of Refactoring (6) Making Method Calls Simpler.
Abteilung für Telekooperation Video Shop Case Übung Softwareentwicklung 2 für Wirtschaftsinformatik Ismail Khalil Ibrahim Wieland Schwinger.
Refactoring (1). Software Evolution Cope with change Feature bloat Design decay Code duplications “Pattern time is refactoring time” Make future changes.
A (Very) Simple Example Consolidate duplicate conditional fragments if (isSpecialDeal()) { total = price * 0.95; send (); } else { total = price * 0.98;
Principles and examples
Catalog of Refactoring
Catalog of Refactoring
Module Road Map Refactoring Why Refactoring? Examples
Outline A. What is Refactoring? B. Why do we Refactor?
Refactoring with inline temp, method, and class
Software Construction and Evolution - CSSE 375 Composing Methods
Overview of Eclipse Lectures
Refactoring.
Reusability 11/29/2018© 2006 ITT Educational Services Inc.
Code Smells 1.
private double amountFor(Rental aRental)
Improving the structure of existing code
Assignment Operators Topics Increment and Decrement Operators
Refactoring.
Presentation transcript:

Refactoring: Improving the Design of Existing Code © Martin Fowler, Martin Fowler

What is Refactoring A series of small steps, each of which changes the program’s internal structure without changing its external behavior

Sample Output Rental Record for Dinsdale Pirhana Monty Python and the Holy Grail 3.5 Ran2 Star Trek 276 Star Wars 3.23 Wallace and Gromit6 Amount owed is 20.5 You earned 6 frequent renter points

Starting Class diagram

Class Movie 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 = priceCode; } public int getPriceCode() { return _priceCode; } public void setPriceCode(int arg) { _priceCode = arg; } public String getTitle () { return _title; }; }

Class Rental class Rental { private Movie _movie; private int _daysRented; public Rental(Movie movie, int daysRented) { _movie = movie; _daysRented = daysRented; } public int getDaysRented() { return _daysRented; } public Movie getMovie() { return _movie; }

Class Customer (almost) class Customer { private String _name; private Vector _rentals = new Vector(); public Customer (String name){ _name = name; }; public void addRental(Rental arg) { _rentals.addElement(arg); } public String getName (){ return _name; }; public String statement() // see next slide

Customer.statement() part 1 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(); //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.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } continues on next slide

Customer.statement() part 2 // add frequent renter points 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; }

Interactions for statement

Requirements Changes »Produce an html version of the statement »The movie classifications will soon change –together with the rules for charging and for frequent renter points

Extract Method You have a code fragment that can be grouped together Turn the fragment into a method whose name explains the purpose of the method.

Candidate Extraction 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(); //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.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } [snip]12

Steps for Extract Method »Create method named after intention of code »Copy extracted code »Look for local variables and parameters –turn into parameter –turn into return value –declare within method »Compile »Replace code fragment with call to new method »Compile and test

Extracting the Amount Calculation private int amountFor(Rental each) { int thisAmount = 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.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } return thisAmount; }

Statement() after extraction 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); // add frequent renter points 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; }

Extracting the amount calculation (2) private double amountFor(Rental each) { double thisAmount = 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.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } return thisAmount; }

Change names of variables private double amountFor(Rental aRental) { double result = 0; switch (aRental.getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (aRental.getDaysRented() > 2) result += (aRental.getDaysRented() - 2) * 1.5; break; case Movie.NEW_RELEASE: result += aRental.getDaysRented() * 3; break; case Movie.CHILDRENS: result += 1.5; if (aRental.getDaysRented() > 3) result += (aRental.getDaysRented() - 3) * 1.5; break; } return result; } Is this important? Is this method in the right place?

Move Method A method is, or will be, using or used by more features of another class than the class it is defined on. Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegation, or remove it altogether.

Steps for Move method »Declare method in target class »Copy and fit code »Set up a reference from the source object to the target »Turn the original method into a delegating method –amountOf(Rental each) {return each.charge()} –Check for overriding methods »Compile and test »Find all users of the method –Adjust them to call method on target »Remove original method »Compile and test

Moving amount() to Rental class Rental... double getCharge() { double result = 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.CHILDRENS: result += 1.5; if (getDaysRented() > 3) result += (getDaysRented() - 3) * 1.5; break; } return result; }

Altered statement class Customer... 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(); // add frequent renter points 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; }

Problems with temps class Customer... 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(); // add frequent renter points 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; }

Replace Temp with Query 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. The new method can then be used in other methods.

Steps for Replace temp with Query »Find temp with a single assignment »Extract Right Hand Side of assignment »Replace all references of temp with new method »Remove declaration and assignment of temp »Compile and test

thisAmount removed 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(); // add frequent renter points 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(each.getCharge()) + "\n"; totalAmount += each.getCharge(); } //add footer lines result += "Amount owed is " + String.valueOf(totalAmount) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; }

Extract and move frequentRenterPoints() class Customer... 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 += each.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) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; }

After moving charge and frequent renter points

More temps to kill class Customer... 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 += each.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) + "\n"; result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points"; return result; }

The new methods class Customer… private double getTotalCharge() { double result = 0; Enumeration rentals = _rentals.elements(); while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); result += each.getCharge(); } return result; } private int getTotalFrequentRenterPoints(){ int result = 0; Enumeration rentals = _rentals.elements(); while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); result += each.getFrequentRenterPoints(); } return result; }

The temps removed public String statement() { Enumeration rentals = _rentals.elements(); String result = "Rental Record for " + getName() + "\n"; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); //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(getTotalFrequentRenterPoints()) + " frequent renter points"; return result; }

After replacing the totals

htmlStatement() public String htmlStatement() { Enumeration rentals = _rentals.elements(); String result = " Rentals for " + getName() + " \n"; while (rentals.hasMoreElements()) { Rental each = (Rental) rentals.nextElement(); //show figures for each rental result += each.getMovie().getTitle()+ ": " + String.valueOf(each.getCharge()) + " \n"; } //add footer lines result += " You owe " + String.valueOf(getTotalCharge()) + " \n"; result += "On this rental you earned " + String.valueOf(getTotalFrequentRenterPoints()) + " frequent renter points "; return result; }

The current getCharge method class Rental... double getCharge() { double result = 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.CHILDRENS: result += 1.5; if (getDaysRented() > 3) result += (getDaysRented() - 3) * 1.5; break; } return result; }

getCharge moved to Movie class Rental... double getCharge() { return _movie.getCharge(_daysRented); } class Movie … double getCharge(int daysRented) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } Do the same with frequentRenterPoints()

Consider inheritance How’s this?

The Two Hats Adding Function »Add new capabilities to the system »Adds new tests »Get the test working Refactoring rDoes not add any new features rDoes not add tests (but may change some) rRestructure the code to remove redundancy Swap frequently between the hats, but only wear one at a time

Why Refactor »To improve the software design –combat’s “bit rot” –makes the program easier to change »To make the software easier to understand –write for people, not the compiler –understand unfamiliar code »To help find bugs –refactor while debugging to clarify the code Refactoring helps you program faster!

When should you refactor? »To add new functionality –refactor existing code until you understand it –refactor the design to make it easy to add »To find bugs –refactor to understand the code »For code reviews –immediate effect of code review –allows for higher level suggestions Don’t set aside time for refactoring, include it in your normal activities